Merge pull request #42262 from cpuguy83/move_libnetwork
Move libnetwork
This commit is contained in:
commit
a77317882d
434 changed files with 35389 additions and 885 deletions
|
@ -166,13 +166,6 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
|||
--mount=type=bind,src=hack/dockerfile/install,target=/tmp/install \
|
||||
PREFIX=/build /tmp/install/install.sh containerd
|
||||
|
||||
FROM dev-base AS proxy
|
||||
ARG LIBNETWORK_COMMIT
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=bind,src=hack/dockerfile/install,target=/tmp/install \
|
||||
PREFIX=/build /tmp/install/install.sh proxy
|
||||
|
||||
FROM base AS golangci_lint
|
||||
ARG GOLANGCI_LINT_COMMIT
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
|
@ -307,7 +300,6 @@ COPY --from=runc /build/ /usr/local/bin/
|
|||
COPY --from=containerd /build/ /usr/local/bin/
|
||||
COPY --from=rootlesskit /build/ /usr/local/bin/
|
||||
COPY --from=vpnkit /build/ /usr/local/bin/
|
||||
COPY --from=proxy /build/ /usr/local/bin/
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
ARG DOCKER_BUILDTAGS
|
||||
ENV DOCKER_BUILDTAGS="${DOCKER_BUILDTAGS}"
|
||||
|
@ -353,7 +345,6 @@ COPY --from=tini /build/ /usr/local/bin/
|
|||
COPY --from=runc /build/ /usr/local/bin/
|
||||
COPY --from=containerd /build/ /usr/local/bin/
|
||||
COPY --from=rootlesskit /build/ /usr/local/bin/
|
||||
COPY --from=proxy /build/ /usr/local/bin/
|
||||
COPY --from=vpnkit /build/ /usr/local/bin/
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
|
||||
|
|
18
Jenkinsfile
vendored
18
Jenkinsfile
vendored
|
@ -121,7 +121,7 @@ pipeline {
|
|||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
||||
docker:${GIT_COMMIT} \
|
||||
hack/make.sh \
|
||||
dynbinary-daemon \
|
||||
dynbinary \
|
||||
test-docker-py
|
||||
'''
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ pipeline {
|
|||
-e DOCKER_GITCOMMIT=${GIT_COMMIT} \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
docker:${GIT_COMMIT} \
|
||||
hack/make.sh binary-daemon
|
||||
hack/make.sh binary
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +180,9 @@ pipeline {
|
|||
// needs to be last stage that calls make.sh for the junit report to work
|
||||
stage("Unit tests") {
|
||||
steps {
|
||||
sh '''
|
||||
sudo modprobe ip6table_filter
|
||||
'''
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
|
@ -319,7 +322,7 @@ pipeline {
|
|||
-e DOCKER_GRAPHDRIVER \
|
||||
docker:${GIT_COMMIT} \
|
||||
hack/make.sh \
|
||||
dynbinary-daemon
|
||||
dynbinary
|
||||
|
||||
# flaky + integration
|
||||
TEST_INTEGRATION_DEST=1 CONTAINER_NAME=${CONTAINER_NAME}-1 TEST_SKIP_INTEGRATION_CLI=1 run_tests test-integration-flaky &
|
||||
|
@ -577,6 +580,9 @@ pipeline {
|
|||
}
|
||||
stage("Unit tests") {
|
||||
steps {
|
||||
sh '''
|
||||
sudo modprobe ip6table_filter
|
||||
'''
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
|
@ -776,6 +782,9 @@ pipeline {
|
|||
}
|
||||
stage("Unit tests") {
|
||||
steps {
|
||||
sh '''
|
||||
sudo modprobe ip6table_filter
|
||||
'''
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
|
@ -972,6 +981,9 @@ pipeline {
|
|||
}
|
||||
stage("Unit tests") {
|
||||
steps {
|
||||
sh '''
|
||||
sudo modprobe ip6table_filter
|
||||
'''
|
||||
sh '''
|
||||
docker run --rm -t --privileged \
|
||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
)
|
||||
|
||||
// Backend is all the methods that need to be implemented
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/libnetwork"
|
||||
netconst "github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
netconst "github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
|
|
@ -17,10 +17,10 @@ import (
|
|||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/daemon/images"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/libnetwork"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/control"
|
||||
|
|
|
@ -10,9 +10,9 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/moby/buildkit/executor"
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
"github.com/moby/buildkit/executor/runcexecutor"
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"errors"
|
||||
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/moby/buildkit/executor"
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
)
|
||||
|
|
|
@ -16,8 +16,8 @@ import (
|
|||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/libcontainerd/supervisor"
|
||||
"github.com/docker/docker/libnetwork/portallocator"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/docker/libnetwork/portallocator"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
|
|
|
@ -3,7 +3,7 @@ package daemon // import "github.com/docker/docker/daemon"
|
|||
import (
|
||||
apitypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
lncluster "github.com/docker/libnetwork/cluster"
|
||||
lncluster "github.com/docker/docker/libnetwork/cluster"
|
||||
)
|
||||
|
||||
// Cluster is the interface for github.com/docker/docker/daemon/cluster.(*Cluster).
|
||||
|
|
|
@ -53,8 +53,8 @@ import (
|
|||
types "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/daemon/cluster/controllers/plugin"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
lncluster "github.com/docker/docker/libnetwork/cluster"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
lncluster "github.com/docker/libnetwork/cluster"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
swarmnode "github.com/docker/swarmkit/node"
|
||||
"github.com/pkg/errors"
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
basictypes "github.com/docker/docker/api/types"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
types "github.com/docker/docker/api/types/swarm"
|
||||
netconst "github.com/docker/libnetwork/datastore"
|
||||
netconst "github.com/docker/docker/libnetwork/datastore"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
)
|
||||
|
|
|
@ -17,11 +17,11 @@ import (
|
|||
containerpkg "github.com/docker/docker/container"
|
||||
clustertypes "github.com/docker/docker/daemon/cluster/provider"
|
||||
networkSettings "github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork/cluster"
|
||||
networktypes "github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/plugin"
|
||||
volumeopts "github.com/docker/docker/volume/service/opts"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/cluster"
|
||||
networktypes "github.com/docker/libnetwork/types"
|
||||
"github.com/docker/swarmkit/agent/exec"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
volumeopts "github.com/docker/docker/volume/service/opts"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/swarmkit/agent/exec"
|
||||
"github.com/docker/swarmkit/api"
|
||||
"github.com/docker/swarmkit/log"
|
||||
|
|
|
@ -20,9 +20,9 @@ import (
|
|||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
clustertypes "github.com/docker/docker/daemon/cluster/provider"
|
||||
netconst "github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
netconst "github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/swarmkit/agent/exec"
|
||||
"github.com/docker/swarmkit/api"
|
||||
"github.com/docker/swarmkit/api/genericresource"
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/swarmkit/agent/exec"
|
||||
"github.com/docker/swarmkit/api"
|
||||
"github.com/docker/swarmkit/log"
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
clustertypes "github.com/docker/docker/daemon/cluster/provider"
|
||||
networktypes "github.com/docker/libnetwork/types"
|
||||
networktypes "github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/swarmkit/agent"
|
||||
"github.com/docker/swarmkit/agent/exec"
|
||||
"github.com/docker/swarmkit/api"
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
types "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/daemon/cluster/executor/container"
|
||||
lncluster "github.com/docker/libnetwork/cluster"
|
||||
lncluster "github.com/docker/docker/libnetwork/cluster"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
swarmallocator "github.com/docker/swarmkit/manager/allocator/cnmallocator"
|
||||
swarmnode "github.com/docker/swarmkit/node"
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/docker/daemon/discovery"
|
||||
"github.com/docker/docker/libnetwork/ipamutils"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/libnetwork/ipamutils"
|
||||
"github.com/spf13/pflag"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
|
|
|
@ -14,15 +14,15 @@ import (
|
|||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
netconst "github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork"
|
||||
netconst "github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@ import (
|
|||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/links"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/moby/sys/mount"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
|
@ -55,6 +55,9 @@ import (
|
|||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork/cluster"
|
||||
nwconfig "github.com/docker/docker/libnetwork/config"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
|
@ -65,9 +68,6 @@ import (
|
|||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/docker/runconfig"
|
||||
volumesservice "github.com/docker/docker/volume/service"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/cluster"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
"github.com/moby/locker"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/docker/libnetwork/resolvconf"
|
||||
"github.com/moby/sys/mount"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"github.com/pkg/errors"
|
||||
|
|
|
@ -10,12 +10,12 @@ import (
|
|||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
_ "github.com/docker/docker/pkg/discovery/memory"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
volumesservice "github.com/docker/docker/volume/service"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
|
|
|
@ -28,6 +28,13 @@ import (
|
|||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/daemon/initlayer"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
nwconfig "github.com/docker/docker/libnetwork/config"
|
||||
"github.com/docker/docker/libnetwork/drivers/bridge"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/libnetwork/netutils"
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
lntypes "github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
|
@ -36,13 +43,6 @@ import (
|
|||
"github.com/docker/docker/pkg/sysinfo"
|
||||
"github.com/docker/docker/runconfig"
|
||||
volumemounts "github.com/docker/docker/volume/mounts"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/drivers/bridge"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/options"
|
||||
lntypes "github.com/docker/libnetwork/types"
|
||||
"github.com/moby/sys/mount"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
|
|
|
@ -14,6 +14,12 @@ import (
|
|||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
nwconfig "github.com/docker/docker/libnetwork/config"
|
||||
"github.com/docker/docker/libnetwork/datastore"
|
||||
winlibnetwork "github.com/docker/docker/libnetwork/drivers/windows"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
|
@ -21,12 +27,6 @@ import (
|
|||
"github.com/docker/docker/pkg/sysinfo"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
winlibnetwork "github.com/docker/libnetwork/drivers/windows"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/container"
|
||||
daemonevents "github.com/docker/docker/daemon/events"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
|
@ -17,18 +17,18 @@ import (
|
|||
clustertypes "github.com/docker/docker/daemon/cluster/provider"
|
||||
internalnetwork "github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
lncluster "github.com/docker/docker/libnetwork/cluster"
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/libnetwork/networkdb"
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
networktypes "github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork"
|
||||
lncluster "github.com/docker/libnetwork/cluster"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/networkdb"
|
||||
"github.com/docker/libnetwork/options"
|
||||
networktypes "github.com/docker/libnetwork/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ package daemon
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
)
|
||||
|
||||
// getEndpointInNetwork returns the container's endpoint to the provided network.
|
||||
|
|
|
@ -10,9 +10,9 @@ import (
|
|||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/libnetwork"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/skip"
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/docker/docker/api/types/filters"
|
||||
timetypes "github.com/docker/docker/api/types/time"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/daemon/images"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/pkg/discovery"
|
||||
_ "github.com/docker/docker/pkg/discovery/memory"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
dockercontainer "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
|
@ -8,8 +8,6 @@ binary_extension() {
|
|||
fi
|
||||
}
|
||||
|
||||
GO_PACKAGE='github.com/docker/docker/cmd/dockerd'
|
||||
BINARY_SHORT_NAME='dockerd'
|
||||
BINARY_NAME="$BINARY_SHORT_NAME-$VERSION"
|
||||
BINARY_EXTENSION="$(binary_extension)"
|
||||
BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION"
|
||||
|
|
14
hack/make/.install
Normal file
14
hack/make/.install
Normal file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
install_binary() {
|
||||
local file="$1"
|
||||
local target="${DOCKER_MAKE_INSTALL_PREFIX:=/usr/local}/bin/"
|
||||
if [ "$(go env GOOS)" == "linux" ]; then
|
||||
echo "Installing $(basename $file) to ${target}"
|
||||
mkdir -p "$target"
|
||||
cp -f -L "$file" "$target"
|
||||
else
|
||||
echo "Install is only supported on linux"
|
||||
return 1
|
||||
fi
|
||||
}
|
|
@ -7,4 +7,5 @@ rm -rf "$DEST"
|
|||
DEST="${DEST}-daemon"
|
||||
ABS_DEST="${ABS_DEST}-daemon"
|
||||
. hack/make/binary-daemon
|
||||
. hack/make/binary-proxy
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@ copy_binaries() {
|
|||
return
|
||||
fi
|
||||
echo "Copying nested executables into $dir"
|
||||
for file in containerd containerd-shim containerd-shim-runc-v2 ctr runc docker-init docker-proxy rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh dockerd-rootless-setuptool.sh; do
|
||||
for file in containerd containerd-shim containerd-shim-runc-v2 ctr runc docker-init rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh dockerd-rootless-setuptool.sh; do
|
||||
cp -f "$(command -v "$file")" "$dir/"
|
||||
if [ "$hash" = "hash" ]; then
|
||||
hash_files "$dir/$file"
|
||||
|
@ -31,5 +31,11 @@ copy_binaries() {
|
|||
}
|
||||
|
||||
[ -z "$KEEPDEST" ] && rm -rf "$DEST"
|
||||
source "${MAKEDIR}/.binary"
|
||||
copy_binaries "$DEST" 'hash'
|
||||
|
||||
(
|
||||
GO_PACKAGE='github.com/docker/docker/cmd/dockerd'
|
||||
BINARY_SHORT_NAME='dockerd'
|
||||
|
||||
source "${MAKEDIR}/.binary"
|
||||
copy_binaries "$DEST" 'hash'
|
||||
)
|
||||
|
|
10
hack/make/binary-proxy
Normal file
10
hack/make/binary-proxy
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
(
|
||||
GO_PACKAGE='github.com/docker/docker/libnetwork/cmd/proxy'
|
||||
BINARY_SHORT_NAME='docker-proxy'
|
||||
|
||||
source "${MAKEDIR}/.binary"
|
||||
)
|
|
@ -30,7 +30,7 @@ for platform in ${DOCKER_CROSSPLATFORMS}; do
|
|||
echo "Cross building: ${DEST}"
|
||||
mkdir -p "${DEST}"
|
||||
ABS_DEST="$(cd "${DEST}" && pwd -P)"
|
||||
source "${MAKEDIR}/binary-daemon"
|
||||
source "${MAKEDIR}/binary"
|
||||
|
||||
source "${MAKEDIR}/cross-platform-dependent"
|
||||
)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
rm -rf "$DEST"
|
||||
|
||||
# This script exists as backwards compatibility for CI
|
||||
(
|
||||
DEST="${DEST}-daemon"
|
||||
ABS_DEST="${ABS_DEST}-daemon"
|
||||
. hack/make/dynbinary-daemon
|
||||
. hack/make/dynbinary-proxy
|
||||
)
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
[ -z "$KEEPDEST" ] && rm -rf "$DEST"
|
||||
|
||||
(
|
||||
export IAMSTATIC='false'
|
||||
export LDFLAGS_STATIC_DOCKER=''
|
||||
export BUILDFLAGS=("${BUILDFLAGS[@]/netgo /}") # disable netgo, since we don't need it for a dynamic binary
|
||||
export BUILDFLAGS=("${BUILDFLAGS[@]/osusergo /}") # ditto for osusergo
|
||||
export BUILDFLAGS=("${BUILDFLAGS[@]/static_build /}") # we're not building a "static" binary here
|
||||
|
||||
GO_PACKAGE='github.com/docker/docker/cmd/dockerd'
|
||||
BINARY_SHORT_NAME='dockerd'
|
||||
source "${MAKEDIR}/.binary"
|
||||
)
|
||||
|
|
15
hack/make/dynbinary-proxy
Normal file
15
hack/make/dynbinary-proxy
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
(
|
||||
export IAMSTATIC='false'
|
||||
export LDFLAGS_STATIC_DOCKER=''
|
||||
export BUILDFLAGS=("${BUILDFLAGS[@]/netgo /}") # disable netgo, since we don't need it for a dynamic binary
|
||||
export BUILDFLAGS=("${BUILDFLAGS[@]/osusergo /}") # ditto for osusergo
|
||||
export BUILDFLAGS=("${BUILDFLAGS[@]/static_build /}") # we're not building a "static" binary here
|
||||
|
||||
GO_PACKAGE='github.com/docker/docker/libnetwork/cmd/proxy'
|
||||
BINARY_SHORT_NAME='docker-proxy'
|
||||
source "${MAKEDIR}/.binary"
|
||||
)
|
|
@ -3,18 +3,7 @@
|
|||
set -e
|
||||
rm -rf "$DEST"
|
||||
|
||||
install_binary() {
|
||||
local file="$1"
|
||||
local target="${DOCKER_MAKE_INSTALL_PREFIX:=/usr/local}/bin/"
|
||||
if [ "$(go env GOOS)" == "linux" ]; then
|
||||
echo "Installing $(basename $file) to ${target}"
|
||||
mkdir -p "$target"
|
||||
cp -f -L "$file" "$target"
|
||||
else
|
||||
echo "Install is only supported on linux"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
source "${MAKEDIR}/.install"
|
||||
|
||||
(
|
||||
DEST="$(dirname $DEST)/binary-daemon"
|
||||
|
|
11
hack/make/install-proxy
Normal file
11
hack/make/install-proxy
Normal file
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
rm -rf "$DEST"
|
||||
|
||||
source "${MAKEDIR}/.install"
|
||||
|
||||
(
|
||||
DEST="$(dirname $DEST)/binary-proxy"
|
||||
install_binary "${DEST}/docker-proxy"
|
||||
)
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# TESTDIRS='./pkg/term' hack/test/unit
|
||||
#
|
||||
set -eu -o pipefail
|
||||
set -eux -o pipefail
|
||||
|
||||
BUILDFLAGS=(-tags 'netgo seccomp libdm_no_deferred_remove')
|
||||
TESTFLAGS+=" -test.timeout=${TIMEOUT:-5m}"
|
||||
|
@ -18,6 +18,11 @@ TESTDIRS="${TESTDIRS:-./...}"
|
|||
exclude_paths='/vendor/|/integration'
|
||||
pkg_list=$(go list $TESTDIRS | grep -vE "($exclude_paths)")
|
||||
|
||||
echo "${pkg_list}" | grep --fixed-strings "libnetwork/drivers/bridge" \
|
||||
&& if ! type docker-proxy; then
|
||||
hack/make.sh binary-proxy install-proxy
|
||||
fi
|
||||
|
||||
mkdir -p bundles
|
||||
gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report.json --junitfile=bundles/junit-report.xml -- \
|
||||
"${BUILDFLAGS[@]}" \
|
||||
|
|
|
@ -25,6 +25,8 @@ linters:
|
|||
skip-dirs:
|
||||
- bundles
|
||||
- docs
|
||||
# TODO: This package should be completely removed
|
||||
- libnetwork/client/mflag
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
|
@ -59,6 +61,11 @@ issues:
|
|||
- text: "G304: Potential file inclusion via variable"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "G306: Expect WriteFile permissions to be 0600 or less"
|
||||
linters:
|
||||
- gosec
|
||||
- text: 'G307: Deferring unsafe method "Close" on type "*os.File"'
|
||||
linters: gosec
|
||||
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
|
@ -86,3 +93,16 @@ issues:
|
|||
- text: "SA1019: httputil.ErrPersistEOF is deprecated"
|
||||
linters:
|
||||
- staticcheck
|
||||
# This code is doing some fun stuff with reflect and it trips up the linter.
|
||||
- text: "field `foo` is unused"
|
||||
path: "libnetwork/options/options_test.go"
|
||||
linters:
|
||||
- structcheck
|
||||
- unused
|
||||
# This field is only used on windows but is defined in a platform agnostic file.
|
||||
# The linter doesn't understand that the field is used.
|
||||
- text: "`resolverOnce` is unused"
|
||||
path: libnetwork/network.go
|
||||
linters:
|
||||
- structcheck
|
||||
- unused
|
|
@ -4,7 +4,7 @@ set -e -o pipefail
|
|||
shfmtflags="-bn -ci -sr"
|
||||
# NOTE: `git grep '^#!'` may also pick up non-shell script files.
|
||||
# Add exceptional files to `egrep -v` if any false-positive is detected.
|
||||
if git grep --name-only '^#!' | egrep -v '(vendor|\.go|Jenkinsfile)' \
|
||||
if git grep --name-only '^#!' | egrep -v '(vendor|\.go|Jenkinsfile|\.py|\.bats)' \
|
||||
| xargs shfmt -d $shfmtflags; then
|
||||
echo 'Congratulations! The shell scripts are properly formatted.'
|
||||
else
|
||||
|
|
|
@ -31,10 +31,10 @@ import (
|
|||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/libnetwork/iptables"
|
||||
"github.com/docker/docker/opts"
|
||||
testdaemon "github.com/docker/docker/testutil/daemon"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/moby/sys/mount"
|
||||
"golang.org/x/sys/unix"
|
||||
|
|
|
@ -18,14 +18,14 @@ import (
|
|||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
remoteapi "github.com/docker/docker/libnetwork/drivers/remote/api"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
remoteipam "github.com/docker/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
testdaemon "github.com/docker/docker/testutil/daemon"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
remoteapi "github.com/docker/libnetwork/drivers/remote/api"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
remoteipam "github.com/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
"gotest.tools/v3/assert"
|
||||
|
|
|
@ -27,13 +27,13 @@ import (
|
|||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/docker/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/testutil"
|
||||
"github.com/docker/docker/testutil/fakecontext"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/icmd"
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
remoteipam "github.com/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
remoteipam "github.com/docker/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/swarmkit/ca/keyutils"
|
||||
"github.com/vishvananda/netlink"
|
||||
"gotest.tools/v3/assert"
|
||||
|
|
|
@ -569,7 +569,7 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
|
|||
var cpDesc *v1.Descriptor
|
||||
for _, m := range index.Manifests {
|
||||
if m.MediaType == images.MediaTypeContainerd1Checkpoint {
|
||||
cpDesc = &m
|
||||
cpDesc = &m // nolint:gosec
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
79
libnetwork/.circleci/config.yml
Normal file
79
libnetwork/.circleci/config.yml
Normal file
|
@ -0,0 +1,79 @@
|
|||
version: 2
|
||||
|
||||
defaults: &defaults
|
||||
working_directory: ~/go/src/github.com/docker/libnetwork
|
||||
docker:
|
||||
# the following image is irrelevant for the build, everything is built inside a container, check the Makefile
|
||||
- image: 'circleci/golang:latest'
|
||||
environment:
|
||||
dockerbuildargs: .
|
||||
dockerargs: --privileged -e CIRCLECI
|
||||
|
||||
jobs:
|
||||
builder:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run: make builder
|
||||
|
||||
build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run: make build
|
||||
|
||||
check:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run: make check
|
||||
|
||||
cross:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run: make cross
|
||||
|
||||
unit-tests:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run: make unit-tests
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
ci:
|
||||
jobs:
|
||||
- builder
|
||||
- build:
|
||||
requires:
|
||||
- builder
|
||||
- check:
|
||||
requires:
|
||||
- builder
|
||||
- cross:
|
||||
requires:
|
||||
- builder
|
||||
- unit-tests:
|
||||
requires:
|
||||
- builder
|
5
libnetwork/.dockerignore
Normal file
5
libnetwork/.dockerignore
Normal file
|
@ -0,0 +1,5 @@
|
|||
.git
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
bin
|
||||
tags
|
45
libnetwork/.gitignore
vendored
Normal file
45
libnetwork/.gitignore
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*~
|
||||
.gtm
|
||||
bin/
|
||||
tags
|
||||
.DS_Store
|
||||
|
||||
# Folders
|
||||
integration-tmp/
|
||||
_obj
|
||||
_test
|
||||
.vagrant
|
||||
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
cmd/dnet/dnet
|
||||
|
||||
# Coverage
|
||||
*.tmp
|
||||
*.coverprofile
|
||||
|
||||
# IDE files and folders
|
||||
.project
|
||||
.settings/
|
||||
|
||||
libnetworkbuild.created
|
||||
test/networkDb/testMain
|
||||
test/networkDb/gossipdb
|
199
libnetwork/CHANGELOG.md
Normal file
199
libnetwork/CHANGELOG.md
Normal file
|
@ -0,0 +1,199 @@
|
|||
# Changelog
|
||||
|
||||
## 0.8.0-dev.2 (2016-05-07)
|
||||
- Fix an issue which may arise during sandbox cleanup (https://github.com/docker/libnetwork/pull/1157)
|
||||
- Fix cleanup logic in case of ipv6 allocation failure
|
||||
- Don't add /etc/hosts record if container's ip is empty (--net=none)
|
||||
- Fix default gw logic for internal networks
|
||||
- Error when updating IPv6 gateway (https://github.com/docker/libnetwork/issues/1142)
|
||||
- Fixes https://github.com/docker/libnetwork/issues/1113
|
||||
- Fixes https://github.com/docker/libnetwork/issues/1069
|
||||
- Fxies https://github.com/docker/libnetwork/issues/1117
|
||||
- Increase the concurrent query rate-limit count
|
||||
- Changes to build libnetwork in Solaris
|
||||
|
||||
## 0.8.0-dev.1 (2016-04-16)
|
||||
- Fixes docker/docker#16964
|
||||
- Added maximum egress bandwidth qos for Windows
|
||||
|
||||
## 0.7.0-rc.6 (2016-04-10)
|
||||
- Flush cached resolver socket on default gateway change
|
||||
|
||||
## 0.7.0-rc.5 (2016-04-08)
|
||||
- Persist ipam driver options
|
||||
- Fixes https://github.com/docker/libnetwork/issues/1087
|
||||
- Use go vet from go tool
|
||||
- Godep update to pick up latest docker/docker packages
|
||||
- Validate remote driver response using docker plugins package method.
|
||||
|
||||
## 0.7.0-rc.4 (2016-04-06)
|
||||
- Fix the handling for default gateway Endpoint join/leave.
|
||||
|
||||
## 0.7.0-rc.3 (2016-04-05)
|
||||
- Revert fix for default gateway endpoint join/leave. Needs to be reworked.
|
||||
- Persist the network internal mode for bridge networks
|
||||
|
||||
## 0.7.0-rc.2 (2016-04-05)
|
||||
- Fixes https://github.com/docker/libnetwork/issues/1070
|
||||
- Move IPAM resource initialization out of init()
|
||||
- Initialize overlay driver before network delete
|
||||
- Fix the handling for default gateway Endpoint join/lean
|
||||
|
||||
## 0.7.0-rc.1 (2016-03-30)
|
||||
- Fixes https://github.com/docker/libnetwork/issues/985
|
||||
- Fixes https://github.com/docker/libnetwork/issues/945
|
||||
- Log time taken to set sandbox key
|
||||
- Limit number of concurrent DNS queries
|
||||
|
||||
## 0.7.0-dev.10 (2016-03-21)
|
||||
- Add IPv6 service discovery (AAAA records) in embedded DNS server
|
||||
- Honor enableIPv6 flag in network create for the IP allocation
|
||||
- Avoid V6 queries in docker domain going to external nameservers
|
||||
|
||||
## 0.7.0-dev.9 (2016-03-18)
|
||||
- Support labels on networks
|
||||
|
||||
## 0.7.0-dev.8 (2016-03-16)
|
||||
- Windows driver to respect user set MAC address.
|
||||
- Fix possible nil pointer reference in ServeDNS() with concurrent go routines.
|
||||
- Fix netns path setting from hook (for containerd integration)
|
||||
- Clear cached udp connections on resolver Stop()
|
||||
- Avoid network/endpoint count inconsistences and remove stale networks after ungraceful shutdown
|
||||
- Fix possible endpoint count inconsistency after ungraceful shutdown
|
||||
- Reject a null v4 IPAM slice in exp vlan drivers
|
||||
- Removed experimental drivers modprobe check
|
||||
|
||||
## 0.7.0-dev.7 (2016-03-11)
|
||||
- Bumped up the minimum kernel version for ipvlan to 4.2
|
||||
- Removed modprobe from macvlan/ipvlan drivers to resolve docker IT failures
|
||||
- Close dbus connection if firewalld is not started
|
||||
|
||||
## 0.7.0-dev.6 (2016-03-10)
|
||||
- Experimental support for macvlan and ipvlan drivers
|
||||
|
||||
## 0.7.0-dev.5 (2016-03-08)
|
||||
- Fixes https://github.com/docker/docker/issues/20847
|
||||
- Fixes https://github.com/docker/docker/issues/20997
|
||||
- Fixes issues unveiled by docker integ test over 0.7.0-dev.4
|
||||
|
||||
## 0.7.0-dev.4 (2016-03-07)
|
||||
- Changed ownership of exposed ports and port-mapping options from Endpoint to Sandbox
|
||||
- Implement DNS RR in the Docker embedded DNS server
|
||||
- Fixes https://github.com/docker/libnetwork/issues/984 (multi container overlay veth leak)
|
||||
- Libnetwork to program container's interface MAC address
|
||||
- Fixed bug in iptables.Exists() logic
|
||||
- Fixes https://github.com/docker/docker/issues/20694
|
||||
- Source external DNS queries from container namespace
|
||||
- Added inbuilt nil IPAM driver
|
||||
- Windows drivers integration fixes
|
||||
- Extract hostname from (hostname.domainname). Related to https://github.com/docker/docker/issues/14282
|
||||
- Fixed race in sandbox statistics read
|
||||
- Fixes https://github.com/docker/libnetwork/issues/892 (docker start fails when ipv6.disable=1)
|
||||
- Fixed error message on bridge network creation conflict
|
||||
|
||||
## 0.7.0-dev.3 (2016-02-17)
|
||||
- Fixes https://github.com/docker/docker/issues/20350
|
||||
- Fixes https://github.com/docker/docker/issues/20145
|
||||
- Initial Windows HNS integration
|
||||
- Allow passing global datastore config to libnetwork after boot
|
||||
- Set Recursion Available bit in DNS query responses
|
||||
- Make sure iptables chains are recreated on firewalld reload
|
||||
|
||||
## 0.7.0-dev.2 (2016-02-11)
|
||||
- Fixes https://github.com/docker/docker/issues/20140
|
||||
|
||||
## 0.7.0-dev.1 (2016-02-10)
|
||||
- Expose EnableIPV6 option
|
||||
- discoverapi refactoring
|
||||
- Fixed a few typos & docs update
|
||||
|
||||
## 0.6.1-rc2 (2016-02-09)
|
||||
- Fixes https://github.com/docker/docker/issues/20132
|
||||
- Fixes https://github.com/docker/docker/issues/20140
|
||||
- Fixes https://github.com/docker/docker/issues/20019
|
||||
|
||||
## 0.6.1-rc1 (2016-02-05)
|
||||
- Fixes https://github.com/docker/docker/issues/20026
|
||||
|
||||
## 0.6.0-rc7 (2016-02-01)
|
||||
- Allow inter-network connections via exposed ports
|
||||
|
||||
## 0.6.0-rc6 (2016-01-30)
|
||||
- Properly fixes https://github.com/docker/docker/issues/18814
|
||||
|
||||
## 0.6.0-rc5 (2016-01-26)
|
||||
- Cleanup stale overlay sandboxes
|
||||
|
||||
## 0.6.0-rc4 (2016-01-25)
|
||||
- Add Endpoints() API to Sandbox interface
|
||||
- Fixed a race-condition in default gateway network creation
|
||||
|
||||
## 0.6.0-rc3 (2016-01-25)
|
||||
- Fixes docker/docker#19576
|
||||
- Fixed embedded DNS to listen in TCP as well
|
||||
- Fixed a race-condition in IPAM to choose non-overlapping subnet for concurrent requests
|
||||
|
||||
## 0.6.0-rc2 (2016-01-21)
|
||||
- Fixes docker/docker#19376
|
||||
- Fixes docker/docker#15819
|
||||
- Fixes libnetwork/#885, Not filter v6 DNS servers from resolv.conf
|
||||
- Fixes docker/docker #19448, also handles the . in service and network names correctly.
|
||||
|
||||
## 0.6.0-rc1 (2016-01-14)
|
||||
- Fixes docker/docker#19404
|
||||
- Fixes the ungraceful daemon restart issue in systemd with remote network plugin
|
||||
(https://github.com/docker/libnetwork/issues/813)
|
||||
|
||||
## 0.5.6 (2016-01-14)
|
||||
- Setup embedded DNS server correctly on container restart. Fixes docker/docker#19354
|
||||
|
||||
## 0.5.5 (2016-01-14)
|
||||
- Allow network-scoped alias to be resolved for anonymous endpoint
|
||||
- Self repair corrupted IP database that could happen in 1.9.0 & 1.9.1
|
||||
- Skip IPTables cleanup if --iptables=false is set. Fixes docker/docker#19063
|
||||
|
||||
## 0.5.4 (2016-01-12)
|
||||
- Removed the isNodeAlive protection when user forces an endpoint delete
|
||||
|
||||
## 0.5.3 (2016-01-12)
|
||||
- Bridge driver supporting internal network option
|
||||
- Backend implementation to support "force" option to network disconnect
|
||||
- Fixing a regex in etchosts package to fix docker/docker#19080
|
||||
|
||||
## 0.5.2 (2016-01-08)
|
||||
- Embedded DNS replacing /etc/hosts based Service Discovery
|
||||
- Container local alias and Network-scoped alias support
|
||||
- Backend support for internal network mode
|
||||
- Support for IPAM driver options
|
||||
- Fixes overlay veth cleanup issue : docker/docker#18814
|
||||
- fixes docker/docker#19139
|
||||
- disable IPv6 Duplicate Address Detection
|
||||
|
||||
## 0.5.1 (2015-12-07)
|
||||
- Allowing user to assign IP Address for containers
|
||||
- Fixes docker/docker#18214
|
||||
- Fixes docker/docker#18380
|
||||
|
||||
## 0.5.0 (2015-10-30)
|
||||
|
||||
- Docker multi-host networking exiting experimental channel
|
||||
- Introduced IP Address Management and IPAM drivers
|
||||
- DEPRECATE service discovery from default bridge network
|
||||
- Introduced new network UX
|
||||
- Support for multiple networks in bridge driver
|
||||
- Local persistence with boltdb
|
||||
|
||||
## 0.4.0 (2015-07-24)
|
||||
|
||||
- Introduce experimental version of Overlay driver
|
||||
- Introduce experimental version of network plugins
|
||||
- Introduce experimental version of network & service UX
|
||||
- Introduced experimental /etc/hosts based service discovery
|
||||
- Integrated with libkv
|
||||
- Improving test coverage
|
||||
- Fixed a bunch of issues with osl namespace mgmt
|
||||
|
||||
## 0.3.0 (2015-05-27)
|
||||
|
||||
- Introduce CNM (Container Networking Model)
|
||||
- Replace docker networking with CNM & Bridge driver
|
22
libnetwork/Dockerfile
Normal file
22
libnetwork/Dockerfile
Normal file
|
@ -0,0 +1,22 @@
|
|||
ARG GO_VERSION=1.13.8
|
||||
|
||||
FROM golang:${GO_VERSION}-buster as dev
|
||||
RUN apt-get update && apt-get -y install iptables \
|
||||
protobuf-compiler
|
||||
|
||||
RUN go get -d github.com/gogo/protobuf/protoc-gen-gogo && \
|
||||
cd /go/src/github.com/gogo/protobuf/protoc-gen-gogo && \
|
||||
git reset --hard 30cf7ac33676b5786e78c746683f0d4cd64fa75b && \
|
||||
go install
|
||||
|
||||
RUN go get golang.org/x/lint/golint \
|
||||
golang.org/x/tools/cmd/cover \
|
||||
github.com/mattn/goveralls \
|
||||
github.com/gordonklaus/ineffassign \
|
||||
github.com/client9/misspell/cmd/misspell
|
||||
|
||||
WORKDIR /go/src/github.com/docker/libnetwork
|
||||
|
||||
FROM dev
|
||||
|
||||
COPY . .
|
52
libnetwork/MAINTAINERS
Normal file
52
libnetwork/MAINTAINERS
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Libnetwork maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/libnetwork project and how.
|
||||
# This is a living document - if you see something out of date or missing, speak up!
|
||||
#
|
||||
# It is structured to be consumable by both humans and programs.
|
||||
# To extract its contents programmatically, use any TOML-compliant parser.
|
||||
#
|
||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||
#
|
||||
[Org]
|
||||
[Org."Core maintainers"]
|
||||
people = [
|
||||
"arkodg",
|
||||
"euanh",
|
||||
"fcrisciani",
|
||||
"mavenugo",
|
||||
"selansen",
|
||||
]
|
||||
|
||||
[people]
|
||||
|
||||
# A reference list of all people associated with the project.
|
||||
# All other sections should refer to people by their canonical key
|
||||
# in the people section.
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
|
||||
[people.arkodg]
|
||||
Name = "Arko Dasgupta"
|
||||
Email = "arko.dasgupta@docker.com"
|
||||
GitHub = "arkodg"
|
||||
|
||||
[people.euanh]
|
||||
Name = "Euan Harris"
|
||||
Email = "euan.harris@docker.com"
|
||||
GitHub = "euanh"
|
||||
|
||||
[people.fcrisciani]
|
||||
Name = "Flavio Crisciani"
|
||||
Email = "flavio.crisciani@docker.com"
|
||||
GitHub = "fcrisciani"
|
||||
|
||||
[people.mavenugo]
|
||||
Name = "Madhu Venugopal"
|
||||
Email = "madhu@docker.com"
|
||||
GitHub = "mavenugo"
|
||||
|
||||
[people.selansen]
|
||||
Name = "Elangovan Sivanandam"
|
||||
Email = "elango.siva@docker.com"
|
||||
GitHub = "selansen"
|
178
libnetwork/Makefile
Normal file
178
libnetwork/Makefile
Normal file
|
@ -0,0 +1,178 @@
|
|||
.PHONY: all all-local build build-local clean cross cross-local vet lint misspell check check-local check-code check-format unit-tests protobuf protobuf-local check-protobuf
|
||||
SHELL=/bin/bash
|
||||
|
||||
dockerbuildargs ?= --target dev - < Dockerfile
|
||||
dockerargs ?= --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork
|
||||
build_image=libnetworkbuild
|
||||
container_env = -e "INSIDECONTAINER=-incontainer=true"
|
||||
docker = docker run --rm -it --init ${dockerargs} $$EXTRA_ARGS ${container_env} ${build_image}
|
||||
|
||||
CROSS_PLATFORMS = linux/amd64 linux/386 linux/arm windows/amd64
|
||||
PACKAGES=$(shell go list ./... | grep -v /vendor/)
|
||||
PROTOC_FLAGS=-I=. -I=/go/src -I=/go/src/github.com/gogo/protobuf -I=/go/src/github.com/gogo/protobuf/protobuf
|
||||
|
||||
export PATH := $(CURDIR)/bin:$(PATH)
|
||||
|
||||
|
||||
# Several targets in this Makefile expect to run within the
|
||||
# libnetworkbuild container. In general, a target named '<target>-local'
|
||||
# relies on utilities inside the build container. Usually there is also
|
||||
# a wrapper called '<target>' which starts a container and runs
|
||||
# 'make <target>-local' inside it.
|
||||
|
||||
###########################################################################
|
||||
# Top level targets
|
||||
###########################################################################
|
||||
|
||||
all: build check clean
|
||||
|
||||
all-local: build-local check-local clean
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Build targets
|
||||
###########################################################################
|
||||
|
||||
# builder builds the libnetworkbuild container. All wrapper targets
|
||||
# must depend on this to ensure that the container exists.
|
||||
builder:
|
||||
DOCKER_BUILDKIT=1 docker build --progress=plain -t ${build_image} --build-arg=GO_VERSION ${dockerbuildargs}
|
||||
|
||||
build: builder
|
||||
@echo "🐳 $@"
|
||||
@${docker} make build-local
|
||||
|
||||
build-local:
|
||||
@echo "🐳 $@"
|
||||
@mkdir -p "bin"
|
||||
go build -tags experimental -o "bin/dnet" ./cmd/dnet
|
||||
go build -o "bin/docker-proxy" ./cmd/proxy
|
||||
CGO_ENABLED=0 go build -o "bin/diagnosticClient" ./cmd/diagnostic
|
||||
CGO_ENABLED=0 go build -o "bin/testMain" ./cmd/networkdb-test/testMain.go
|
||||
|
||||
build-images:
|
||||
@echo "🐳 $@"
|
||||
cp cmd/diagnostic/daemon.json ./bin
|
||||
DOCKER_BUILDKIT=1 docker build --progress=plain -f cmd/diagnostic/Dockerfile.client -t dockereng/network-diagnostic:onlyclient bin/
|
||||
DOCKER_BUILDKIT=1 docker build --progress=plain -f cmd/diagnostic/Dockerfile.dind -t dockereng/network-diagnostic:17.12-dind bin/
|
||||
DOCKER_BUILDKIT=1 docker build --progress=plain -f cmd/networkdb-test/Dockerfile -t dockereng/e2e-networkdb:master bin/
|
||||
DOCKER_BUILDKIT=1 docker build --progress=plain -t dockereng/network-diagnostic:support.sh support/
|
||||
|
||||
push-images: build-images
|
||||
@echo "🐳 $@"
|
||||
docker push dockereng/network-diagnostic:onlyclient
|
||||
docker push dockereng/network-diagnostic:17.12-dind
|
||||
docker push dockereng/e2e-networkdb:master
|
||||
docker push dockereng/network-diagnostic:support.sh
|
||||
|
||||
clean:
|
||||
@echo "🐳 $@"
|
||||
@if [ -d bin ]; then \
|
||||
echo "Removing binaries"; \
|
||||
rm -rf bin; \
|
||||
fi
|
||||
|
||||
cross: builder
|
||||
@mkdir -p "bin"
|
||||
@for platform in ${CROSS_PLATFORMS}; do \
|
||||
EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
|
||||
echo "$${platform}..." ; \
|
||||
${docker} make cross-local ; \
|
||||
done
|
||||
|
||||
cross-local:
|
||||
@echo "🐳 $@"
|
||||
go build -o "bin/dnet-$$GOOS-$$GOARCH" ./cmd/dnet
|
||||
go build -o "bin/docker-proxy-$$GOOS-$$GOARCH" ./cmd/proxy
|
||||
|
||||
# Rebuild protocol buffers.
|
||||
# These may need to be rebuilt after vendoring updates, so .proto files are declared .PHONY so they are always rebuilt.
|
||||
PROTO_FILES=$(shell find . -path ./vendor -prune -o -name \*.proto -print)
|
||||
PB_FILES=$(PROTO_FILES:.proto=.pb.go)
|
||||
|
||||
# Pattern rule for protoc. If PROTOC_CHECK is defined, it checks
|
||||
# whether the generated files are up to date and fails if they are not
|
||||
%.pb.go: %.proto
|
||||
@if [ ${PROTOC_CHECK} ]; then \
|
||||
protoc ${PROTOC_FLAGS} --gogo_out=/tmp $< ; \
|
||||
diff -q $@ /tmp/$@ >/dev/null || (echo "👹 $@ is out of date; please run 'make protobuf' and check in updates" && exit 1) ; \
|
||||
else \
|
||||
protoc ${PROTOC_FLAGS} --gogo_out=./ $< ; \
|
||||
fi
|
||||
|
||||
.PHONY: $(PROTO_FILES)
|
||||
protobuf: builder
|
||||
@${docker} make protobuf-local
|
||||
protobuf-local: $(PB_FILES)
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Test targets
|
||||
###########################################################################
|
||||
|
||||
check: builder
|
||||
@${docker} make check-local
|
||||
|
||||
check-local: check-code check-format
|
||||
|
||||
check-code: check-protobuf lint vet ineffassign
|
||||
|
||||
check-format: fmt misspell
|
||||
|
||||
unit-tests: builder
|
||||
${docker} make unit-tests-local
|
||||
|
||||
unit-tests-local:
|
||||
@echo "🐳 Running tests... "
|
||||
@echo "mode: count" > coverage.coverprofile
|
||||
@go build -o "bin/docker-proxy" ./cmd/proxy
|
||||
@for dir in $$( find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -not -path './vendor/*' -type d); do \
|
||||
if ls $$dir/*.go &> /dev/null; then \
|
||||
pushd . &> /dev/null ; \
|
||||
cd $$dir ; \
|
||||
go test ${INSIDECONTAINER} -test.parallel 5 -test.v -covermode=count -coverprofile=./profile.tmp ; \
|
||||
ret=$$? ;\
|
||||
if [ $$ret -ne 0 ]; then exit $$ret; fi ;\
|
||||
popd &> /dev/null; \
|
||||
if [ -f $$dir/profile.tmp ]; then \
|
||||
cat $$dir/profile.tmp | tail -n +2 >> coverage.coverprofile ; \
|
||||
rm $$dir/profile.tmp ; \
|
||||
fi ; \
|
||||
fi ; \
|
||||
done
|
||||
@echo "Done running tests"
|
||||
|
||||
# Depends on binaries because vet will silently fail if it can not load compiled imports
|
||||
vet: ## run go vet
|
||||
@echo "🐳 $@"
|
||||
@test -z "$$(go vet ${PACKAGES} 2>&1 | grep -v 'constant [0-9]* not a string in call to Errorf' | egrep -v '(timestamp_test.go|duration_test.go|exit status 1)' | tee /dev/stderr)"
|
||||
|
||||
misspell:
|
||||
@echo "🐳 $@"
|
||||
@test -z "$$(find . -type f | grep -v vendor/ | grep "\.go\|\.md" | xargs misspell -error | tee /dev/stderr)"
|
||||
|
||||
fmt: ## run go fmt
|
||||
@echo "🐳 $@"
|
||||
@test -z "$$(gofmt -s -l . | grep -v vendor/ | grep -v ".pb.go$$" | tee /dev/stderr)" || \
|
||||
(echo "👹 please format Go code with 'gofmt -s -w'" && false)
|
||||
|
||||
lint: ## run go lint
|
||||
@echo "🐳 $@"
|
||||
@test -z "$$(golint ./... | grep -v vendor/ | grep -v ".pb.go:" | grep -v ".mock.go" | tee /dev/stderr)"
|
||||
|
||||
ineffassign: ## run ineffassign
|
||||
@echo "🐳 $@"
|
||||
@test -z "$$(ineffassign . | grep -v vendor/ | grep -v ".pb.go:" | grep -v ".mock.go" | tee /dev/stderr)"
|
||||
|
||||
# check-protobuf rebuilds .pb.go files and fails if they have changed
|
||||
check-protobuf: PROTOC_CHECK=1
|
||||
check-protobuf: $(PB_FILES)
|
||||
@echo "🐳 $@"
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Utility targets
|
||||
###########################################################################
|
||||
|
||||
shell: builder
|
||||
@${docker} ${SHELL}
|
58
libnetwork/Vagrantfile
vendored
Normal file
58
libnetwork/Vagrantfile
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
$consul=<<SCRIPT
|
||||
apt-get update
|
||||
apt-get -y install wget
|
||||
wget -qO- https://experimental.docker.com/ | sh
|
||||
gpasswd -a vagrant docker
|
||||
service docker restart
|
||||
docker run -d -p 8500:8500 -p 8300-8302:8300-8302/tcp -p 8300-8302:8300-8302/udp -h consul progrium/consul -server -bootstrap
|
||||
SCRIPT
|
||||
|
||||
$bootstrap=<<SCRIPT
|
||||
apt-get update
|
||||
apt-get -y install wget curl
|
||||
apt-get -y install bridge-utils
|
||||
wget -qO- https://experimental.docker.com/ | sh
|
||||
gpasswd -a vagrant docker
|
||||
echo DOCKER_OPTS=\\"--cluster-store=consul://192.168.33.10:8500 --cluster-advertise=${1}:0\\" >> /etc/default/docker
|
||||
cp /vagrant/docs/vagrant-systemd/docker.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl restart docker.service
|
||||
SCRIPT
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'"
|
||||
num_nodes = 2
|
||||
base_ip = "192.168.33."
|
||||
net_ips = num_nodes.times.collect { |n| base_ip + "#{n+11}" }
|
||||
|
||||
config.vm.define "consul-server" do |consul|
|
||||
consul.vm.box = "ubuntu/trusty64"
|
||||
consul.vm.hostname = "consul-server"
|
||||
consul.vm.network :private_network, ip: "192.168.33.10"
|
||||
consul.vm.provider "virtualbox" do |vb|
|
||||
vb.customize ["modifyvm", :id, "--memory", "512"]
|
||||
end
|
||||
consul.vm.provision :shell, inline: $consul
|
||||
end
|
||||
|
||||
num_nodes.times do |n|
|
||||
config.vm.define "net-#{n+1}" do |net|
|
||||
net.vm.box = "ubuntu/xenial64"
|
||||
net_ip = net_ips[n]
|
||||
net_index = n+1
|
||||
net.vm.hostname = "net-#{net_index}"
|
||||
net.vm.provider "virtualbox" do |vb|
|
||||
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||
end
|
||||
net.vm.network :private_network, ip: "#{net_ip}"
|
||||
net.vm.provision :shell, inline: $bootstrap, :args => "#{net_ip}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
package libnetwork
|
||||
|
||||
//go:generate protoc -I.:Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/libnetwork,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. agent.proto
|
||||
//go:generate protoc -I.:Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/docker/libnetwork,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. agent.proto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -9,13 +9,13 @@ import (
|
|||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/libnetwork/cluster"
|
||||
"github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
"github.com/docker/docker/libnetwork/networkdb"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/go-events"
|
||||
"github.com/docker/libnetwork/cluster"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/networkdb"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -793,7 +793,7 @@ func (n *network) addDriverWatches() {
|
|||
return
|
||||
}
|
||||
|
||||
agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte, deleted bool) bool {
|
||||
err = agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte, deleted bool) bool {
|
||||
// skip the entries that are mark for deletion, this is safe because this function is
|
||||
// called at initialization time so there is no state to delete
|
||||
if nid == n.ID() && !deleted {
|
||||
|
@ -801,6 +801,9 @@ func (n *network) addDriverWatches() {
|
|||
}
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
logrus.WithError(err).Warn("Error while walking networkdb")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
973
libnetwork/api/api.go
Normal file
973
libnetwork/api/api.go
Normal file
|
@ -0,0 +1,973 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/libnetwork/netutils"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var (
|
||||
successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK}
|
||||
createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
|
||||
badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest}
|
||||
)
|
||||
|
||||
const (
|
||||
// Resource name regex
|
||||
// Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks
|
||||
// to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`)
|
||||
regex = "[a-zA-Z_0-9-]+"
|
||||
qregx = "$|" + regex
|
||||
// Router URL variable definition
|
||||
nwNameQr = "{" + urlNwName + ":" + qregx + "}"
|
||||
nwID = "{" + urlNwID + ":" + regex + "}"
|
||||
nwPIDQr = "{" + urlNwPID + ":" + qregx + "}"
|
||||
epNameQr = "{" + urlEpName + ":" + qregx + "}"
|
||||
epID = "{" + urlEpID + ":" + regex + "}"
|
||||
epPIDQr = "{" + urlEpPID + ":" + qregx + "}"
|
||||
sbID = "{" + urlSbID + ":" + regex + "}"
|
||||
sbPIDQr = "{" + urlSbPID + ":" + qregx + "}"
|
||||
cnIDQr = "{" + urlCnID + ":" + qregx + "}"
|
||||
cnPIDQr = "{" + urlCnPID + ":" + qregx + "}"
|
||||
|
||||
// Internal URL variable name.They can be anything as
|
||||
// long as they do not collide with query fields.
|
||||
urlNwName = "network-name"
|
||||
urlNwID = "network-id"
|
||||
urlNwPID = "network-partial-id"
|
||||
urlEpName = "endpoint-name"
|
||||
urlEpID = "endpoint-id"
|
||||
urlEpPID = "endpoint-partial-id"
|
||||
urlSbID = "sandbox-id"
|
||||
urlSbPID = "sandbox-partial-id"
|
||||
urlCnID = "container-id"
|
||||
urlCnPID = "container-partial-id"
|
||||
)
|
||||
|
||||
// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
|
||||
func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) {
|
||||
h := &httpHandler{c: c}
|
||||
h.initRouter()
|
||||
return h.handleRequest
|
||||
}
|
||||
|
||||
type responseStatus struct {
|
||||
Status string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (r *responseStatus) isOK() bool {
|
||||
return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated
|
||||
}
|
||||
|
||||
type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus)
|
||||
|
||||
type httpHandler struct {
|
||||
c libnetwork.NetworkController
|
||||
r *mux.Router
|
||||
}
|
||||
|
||||
func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
|
||||
// Make sure the service is there
|
||||
if h.c == nil {
|
||||
http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
// Get handler from router and execute it
|
||||
h.r.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func (h *httpHandler) initRouter() {
|
||||
m := map[string][]struct {
|
||||
url string
|
||||
qrs []string
|
||||
fct processor
|
||||
}{
|
||||
"GET": {
|
||||
// Order matters
|
||||
{"/networks", []string{"name", nwNameQr}, procGetNetworks},
|
||||
{"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks},
|
||||
{"/networks", nil, procGetNetworks},
|
||||
{"/networks/" + nwID, nil, procGetNetwork},
|
||||
{"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints},
|
||||
{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints},
|
||||
{"/networks/" + nwID + "/endpoints", nil, procGetEndpoints},
|
||||
{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
|
||||
{"/services", []string{"network", nwNameQr}, procGetServices},
|
||||
{"/services", []string{"name", epNameQr}, procGetServices},
|
||||
{"/services", []string{"partial-id", epPIDQr}, procGetServices},
|
||||
{"/services", nil, procGetServices},
|
||||
{"/services/" + epID, nil, procGetService},
|
||||
{"/services/" + epID + "/backend", nil, procGetSandbox},
|
||||
{"/sandboxes", []string{"partial-container-id", cnPIDQr}, procGetSandboxes},
|
||||
{"/sandboxes", []string{"container-id", cnIDQr}, procGetSandboxes},
|
||||
{"/sandboxes", []string{"partial-id", sbPIDQr}, procGetSandboxes},
|
||||
{"/sandboxes", nil, procGetSandboxes},
|
||||
{"/sandboxes/" + sbID, nil, procGetSandbox},
|
||||
},
|
||||
"POST": {
|
||||
{"/networks", nil, procCreateNetwork},
|
||||
{"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint},
|
||||
{"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes", nil, procJoinEndpoint},
|
||||
{"/services", nil, procPublishService},
|
||||
{"/services/" + epID + "/backend", nil, procAttachBackend},
|
||||
{"/sandboxes", nil, procCreateSandbox},
|
||||
},
|
||||
"DELETE": {
|
||||
{"/networks/" + nwID, nil, procDeleteNetwork},
|
||||
{"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint},
|
||||
{"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes/" + sbID, nil, procLeaveEndpoint},
|
||||
{"/services/" + epID, nil, procUnpublishService},
|
||||
{"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend},
|
||||
{"/sandboxes/" + sbID, nil, procDeleteSandbox},
|
||||
},
|
||||
}
|
||||
|
||||
h.r = mux.NewRouter()
|
||||
for method, routes := range m {
|
||||
for _, route := range routes {
|
||||
r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
|
||||
if route.qrs != nil {
|
||||
r.Queries(route.qrs...)
|
||||
}
|
||||
|
||||
r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
|
||||
if route.qrs != nil {
|
||||
r.Queries(route.qrs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
body []byte
|
||||
err error
|
||||
)
|
||||
if req.Body != nil {
|
||||
body, err = ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res, rsp := fct(ctrl, mux.Vars(req), body)
|
||||
if !rsp.isOK() {
|
||||
http.Error(w, rsp.Status, rsp.StatusCode)
|
||||
return
|
||||
}
|
||||
if res != nil {
|
||||
writeJSON(w, rsp.StatusCode, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************
|
||||
Resource Builders
|
||||
******************/
|
||||
|
||||
func buildNetworkResource(nw libnetwork.Network) *networkResource {
|
||||
r := &networkResource{}
|
||||
if nw != nil {
|
||||
r.Name = nw.Name()
|
||||
r.ID = nw.ID()
|
||||
r.Type = nw.Type()
|
||||
epl := nw.Endpoints()
|
||||
r.Endpoints = make([]*endpointResource, 0, len(epl))
|
||||
for _, e := range epl {
|
||||
epr := buildEndpointResource(e)
|
||||
r.Endpoints = append(r.Endpoints, epr)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
|
||||
r := &endpointResource{}
|
||||
if ep != nil {
|
||||
r.Name = ep.Name()
|
||||
r.ID = ep.ID()
|
||||
r.Network = ep.Network()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource {
|
||||
r := &sandboxResource{}
|
||||
if sb != nil {
|
||||
r.ID = sb.ID()
|
||||
r.Key = sb.Key()
|
||||
r.ContainerID = sb.ContainerID()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
/****************
|
||||
Options Parsers
|
||||
*****************/
|
||||
|
||||
func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption {
|
||||
var setFctList []libnetwork.SandboxOption
|
||||
if sc.HostName != "" {
|
||||
setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName))
|
||||
}
|
||||
if sc.DomainName != "" {
|
||||
setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName))
|
||||
}
|
||||
if sc.HostsPath != "" {
|
||||
setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath))
|
||||
}
|
||||
if sc.ResolvConfPath != "" {
|
||||
setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath))
|
||||
}
|
||||
if sc.UseDefaultSandbox {
|
||||
setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox())
|
||||
}
|
||||
if sc.UseExternalKey {
|
||||
setFctList = append(setFctList, libnetwork.OptionUseExternalKey())
|
||||
}
|
||||
if sc.DNS != nil {
|
||||
for _, d := range sc.DNS {
|
||||
setFctList = append(setFctList, libnetwork.OptionDNS(d))
|
||||
}
|
||||
}
|
||||
if sc.ExtraHosts != nil {
|
||||
for _, e := range sc.ExtraHosts {
|
||||
setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address))
|
||||
}
|
||||
}
|
||||
if sc.ExposedPorts != nil {
|
||||
setFctList = append(setFctList, libnetwork.OptionExposedPorts(sc.ExposedPorts))
|
||||
}
|
||||
if sc.PortMapping != nil {
|
||||
setFctList = append(setFctList, libnetwork.OptionPortMapping(sc.PortMapping))
|
||||
}
|
||||
return setFctList
|
||||
}
|
||||
|
||||
/******************
|
||||
Process functions
|
||||
*******************/
|
||||
|
||||
func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) {
|
||||
if nc.NetworkType == "" {
|
||||
nc.NetworkType = c.Config().Daemon.DefaultDriver
|
||||
}
|
||||
}
|
||||
|
||||
/***************************
|
||||
NetworkController interface
|
||||
****************************/
|
||||
func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var create networkCreate
|
||||
|
||||
err := json.Unmarshal(body, &create)
|
||||
if err != nil {
|
||||
return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
processCreateDefaults(c, &create)
|
||||
|
||||
options := []libnetwork.NetworkOption{}
|
||||
if val, ok := create.NetworkOpts[netlabel.Internal]; ok {
|
||||
internal, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
if internal {
|
||||
options = append(options, libnetwork.NetworkOptionInternalNetwork())
|
||||
}
|
||||
}
|
||||
if val, ok := create.NetworkOpts[netlabel.EnableIPv6]; ok {
|
||||
enableIPv6, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
options = append(options, libnetwork.NetworkOptionEnableIPv6(enableIPv6))
|
||||
}
|
||||
if len(create.DriverOpts) > 0 {
|
||||
options = append(options, libnetwork.NetworkOptionDriverOpts(create.DriverOpts))
|
||||
}
|
||||
|
||||
if len(create.IPv4Conf) > 0 {
|
||||
ipamV4Conf := &libnetwork.IpamConf{
|
||||
PreferredPool: create.IPv4Conf[0].PreferredPool,
|
||||
SubPool: create.IPv4Conf[0].SubPool,
|
||||
}
|
||||
|
||||
options = append(options, libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil))
|
||||
}
|
||||
|
||||
nw, err := c.NewNetwork(create.NetworkType, create.Name, create.ID, options...)
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return nw.ID(), &createdResponse
|
||||
}
|
||||
|
||||
func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
t, by := detectNetworkTarget(vars)
|
||||
nw, errRsp := findNetwork(c, t, by)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
return buildNetworkResource(nw), &successResponse
|
||||
}
|
||||
|
||||
func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var list []*networkResource
|
||||
|
||||
// Look for query filters and validate
|
||||
name, queryByName := vars[urlNwName]
|
||||
shortID, queryByPid := vars[urlNwPID]
|
||||
if queryByName && queryByPid {
|
||||
return nil, &badQueryResponse
|
||||
}
|
||||
|
||||
if queryByName {
|
||||
if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
|
||||
list = append(list, buildNetworkResource(nw))
|
||||
}
|
||||
} else if queryByPid {
|
||||
// Return all the prefix-matching networks
|
||||
l := func(nw libnetwork.Network) bool {
|
||||
if strings.HasPrefix(nw.ID(), shortID) {
|
||||
list = append(list, buildNetworkResource(nw))
|
||||
}
|
||||
return false
|
||||
}
|
||||
c.WalkNetworks(l)
|
||||
} else {
|
||||
for _, nw := range c.Networks() {
|
||||
list = append(list, buildNetworkResource(nw))
|
||||
}
|
||||
}
|
||||
|
||||
return list, &successResponse
|
||||
}
|
||||
|
||||
func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var create sandboxCreate
|
||||
|
||||
err := json.Unmarshal(body, &create)
|
||||
if err != nil {
|
||||
return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
|
||||
sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...)
|
||||
if err != nil {
|
||||
return "", convertNetworkError(err)
|
||||
}
|
||||
|
||||
return sb.ID(), &createdResponse
|
||||
}
|
||||
|
||||
/******************
|
||||
Network interface
|
||||
*******************/
|
||||
func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var ec endpointCreate
|
||||
|
||||
err := json.Unmarshal(body, &ec)
|
||||
if err != nil {
|
||||
return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
|
||||
nwT, nwBy := detectNetworkTarget(vars)
|
||||
n, errRsp := findNetwork(c, nwT, nwBy)
|
||||
if !errRsp.isOK() {
|
||||
return "", errRsp
|
||||
}
|
||||
|
||||
var setFctList []libnetwork.EndpointOption
|
||||
for _, str := range ec.MyAliases {
|
||||
setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str))
|
||||
}
|
||||
|
||||
ep, err := n.CreateEndpoint(ec.Name, setFctList...)
|
||||
if err != nil {
|
||||
return "", convertNetworkError(err)
|
||||
}
|
||||
|
||||
return ep.ID(), &createdResponse
|
||||
}
|
||||
|
||||
func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
nwT, nwBy := detectNetworkTarget(vars)
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
|
||||
ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
return buildEndpointResource(ep), &successResponse
|
||||
}
|
||||
|
||||
func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
// Look for query filters and validate
|
||||
name, queryByName := vars[urlEpName]
|
||||
shortID, queryByPid := vars[urlEpPID]
|
||||
if queryByName && queryByPid {
|
||||
return nil, &badQueryResponse
|
||||
}
|
||||
|
||||
nwT, nwBy := detectNetworkTarget(vars)
|
||||
nw, errRsp := findNetwork(c, nwT, nwBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
var list []*endpointResource
|
||||
|
||||
// If query parameter is specified, return a filtered collection
|
||||
if queryByName {
|
||||
if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
|
||||
list = append(list, buildEndpointResource(ep))
|
||||
}
|
||||
} else if queryByPid {
|
||||
// Return all the prefix-matching endpoints
|
||||
l := func(ep libnetwork.Endpoint) bool {
|
||||
if strings.HasPrefix(ep.ID(), shortID) {
|
||||
list = append(list, buildEndpointResource(ep))
|
||||
}
|
||||
return false
|
||||
}
|
||||
nw.WalkEndpoints(l)
|
||||
} else {
|
||||
for _, ep := range nw.Endpoints() {
|
||||
epr := buildEndpointResource(ep)
|
||||
list = append(list, epr)
|
||||
}
|
||||
}
|
||||
|
||||
return list, &successResponse
|
||||
}
|
||||
|
||||
func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
target, by := detectNetworkTarget(vars)
|
||||
|
||||
nw, errRsp := findNetwork(c, target, by)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
err := nw.Delete()
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return nil, &successResponse
|
||||
}
|
||||
|
||||
/******************
|
||||
Endpoint interface
|
||||
*******************/
|
||||
func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var ej endpointJoin
|
||||
var setFctList []libnetwork.EndpointOption
|
||||
err := json.Unmarshal(body, &ej)
|
||||
if err != nil {
|
||||
return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
|
||||
nwT, nwBy := detectNetworkTarget(vars)
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
|
||||
ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
sb, errRsp := findSandbox(c, ej.SandboxID, byID)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
for _, str := range ej.Aliases {
|
||||
name, alias, err := netutils.ParseAlias(str)
|
||||
if err != nil {
|
||||
return "", convertNetworkError(err)
|
||||
}
|
||||
setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias))
|
||||
}
|
||||
|
||||
err = ep.Join(sb, setFctList...)
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
return sb.Key(), &successResponse
|
||||
}
|
||||
|
||||
func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
nwT, nwBy := detectNetworkTarget(vars)
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
|
||||
ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
sb, errRsp := findSandbox(c, vars[urlSbID], byID)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
err := ep.Leave(sb)
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return nil, &successResponse
|
||||
}
|
||||
|
||||
func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
nwT, nwBy := detectNetworkTarget(vars)
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
|
||||
ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
err := ep.Delete(false)
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return nil, &successResponse
|
||||
}
|
||||
|
||||
/******************
|
||||
Service interface
|
||||
*******************/
|
||||
func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
// Look for query filters and validate
|
||||
nwName, filterByNwName := vars[urlNwName]
|
||||
svName, queryBySvName := vars[urlEpName]
|
||||
shortID, queryBySvPID := vars[urlEpPID]
|
||||
|
||||
if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID {
|
||||
return nil, &badQueryResponse
|
||||
}
|
||||
|
||||
var list []*endpointResource
|
||||
|
||||
switch {
|
||||
case filterByNwName:
|
||||
// return all service present on the specified network
|
||||
nw, errRsp := findNetwork(c, nwName, byName)
|
||||
if !errRsp.isOK() {
|
||||
return list, &successResponse
|
||||
}
|
||||
for _, ep := range nw.Endpoints() {
|
||||
epr := buildEndpointResource(ep)
|
||||
list = append(list, epr)
|
||||
}
|
||||
case queryBySvName:
|
||||
// Look in each network for the service with the specified name
|
||||
l := func(ep libnetwork.Endpoint) bool {
|
||||
if ep.Name() == svName {
|
||||
list = append(list, buildEndpointResource(ep))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, nw := range c.Networks() {
|
||||
nw.WalkEndpoints(l)
|
||||
}
|
||||
case queryBySvPID:
|
||||
// Return all the prefix-matching services
|
||||
l := func(ep libnetwork.Endpoint) bool {
|
||||
if strings.HasPrefix(ep.ID(), shortID) {
|
||||
list = append(list, buildEndpointResource(ep))
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, nw := range c.Networks() {
|
||||
nw.WalkEndpoints(l)
|
||||
}
|
||||
default:
|
||||
for _, nw := range c.Networks() {
|
||||
for _, ep := range nw.Endpoints() {
|
||||
epr := buildEndpointResource(ep)
|
||||
list = append(list, epr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list, &successResponse
|
||||
}
|
||||
|
||||
func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
sv, errRsp := findService(c, epT, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, endpointToService(errRsp)
|
||||
}
|
||||
return buildEndpointResource(sv), &successResponse
|
||||
}
|
||||
|
||||
func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var sp servicePublish
|
||||
|
||||
err := json.Unmarshal(body, &sp)
|
||||
if err != nil {
|
||||
return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
|
||||
n, errRsp := findNetwork(c, sp.Network, byName)
|
||||
if !errRsp.isOK() {
|
||||
return "", errRsp
|
||||
}
|
||||
|
||||
var setFctList []libnetwork.EndpointOption
|
||||
for _, str := range sp.MyAliases {
|
||||
setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str))
|
||||
}
|
||||
|
||||
ep, err := n.CreateEndpoint(sp.Name, setFctList...)
|
||||
if err != nil {
|
||||
return "", endpointToService(convertNetworkError(err))
|
||||
}
|
||||
|
||||
return ep.ID(), &createdResponse
|
||||
}
|
||||
|
||||
func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var sd serviceDelete
|
||||
|
||||
if body != nil {
|
||||
err := json.Unmarshal(body, &sd)
|
||||
if err != nil {
|
||||
return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
}
|
||||
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
sv, errRsp := findService(c, epT, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
if err := sv.Delete(sd.Force); err != nil {
|
||||
return nil, endpointToService(convertNetworkError(err))
|
||||
}
|
||||
return nil, &successResponse
|
||||
}
|
||||
|
||||
func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var bk endpointJoin
|
||||
var setFctList []libnetwork.EndpointOption
|
||||
err := json.Unmarshal(body, &bk)
|
||||
if err != nil {
|
||||
return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
sv, errRsp := findService(c, epT, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
sb, errRsp := findSandbox(c, bk.SandboxID, byID)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
for _, str := range bk.Aliases {
|
||||
name, alias, err := netutils.ParseAlias(str)
|
||||
if err != nil {
|
||||
return "", convertNetworkError(err)
|
||||
}
|
||||
setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias))
|
||||
}
|
||||
|
||||
err = sv.Join(sb, setFctList...)
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return sb.Key(), &successResponse
|
||||
}
|
||||
|
||||
func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
epT, epBy := detectEndpointTarget(vars)
|
||||
sv, errRsp := findService(c, epT, epBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
sb, errRsp := findSandbox(c, vars[urlSbID], byID)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
err := sv.Leave(sb)
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return nil, &successResponse
|
||||
}
|
||||
|
||||
/******************
|
||||
Sandbox interface
|
||||
*******************/
|
||||
func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
if epT, ok := vars[urlEpID]; ok {
|
||||
sv, errRsp := findService(c, epT, byID)
|
||||
if !errRsp.isOK() {
|
||||
return nil, endpointToService(errRsp)
|
||||
}
|
||||
return buildSandboxResource(sv.Info().Sandbox()), &successResponse
|
||||
}
|
||||
|
||||
sbT, by := detectSandboxTarget(vars)
|
||||
sb, errRsp := findSandbox(c, sbT, by)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
return buildSandboxResource(sb), &successResponse
|
||||
}
|
||||
|
||||
type cndFnMkr func(string) cndFn
|
||||
type cndFn func(libnetwork.Sandbox) bool
|
||||
|
||||
// list of (query type, condition function makers) couples
|
||||
var cndMkrList = []struct {
|
||||
identifier string
|
||||
maker cndFnMkr
|
||||
}{
|
||||
{urlSbPID, func(id string) cndFn {
|
||||
return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) }
|
||||
}},
|
||||
{urlCnID, func(id string) cndFn {
|
||||
return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id }
|
||||
}},
|
||||
{urlCnPID, func(id string) cndFn {
|
||||
return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) }
|
||||
}},
|
||||
}
|
||||
|
||||
func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool {
|
||||
for _, im := range cndMkrList {
|
||||
if val, ok := vars[im.identifier]; ok {
|
||||
return im.maker(val)
|
||||
}
|
||||
}
|
||||
return func(sb libnetwork.Sandbox) bool { return true }
|
||||
}
|
||||
|
||||
func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker {
|
||||
return func(sb libnetwork.Sandbox) bool {
|
||||
if condition(sb) {
|
||||
*list = append(*list, buildSandboxResource(sb))
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
var list []*sandboxResource
|
||||
|
||||
cnd := getQueryCondition(vars)
|
||||
c.WalkSandboxes(sandboxWalker(cnd, &list))
|
||||
|
||||
return list, &successResponse
|
||||
}
|
||||
|
||||
func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||
sbT, by := detectSandboxTarget(vars)
|
||||
|
||||
sb, errRsp := findSandbox(c, sbT, by)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
|
||||
err := sb.Delete()
|
||||
if err != nil {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
|
||||
return nil, &successResponse
|
||||
}
|
||||
|
||||
/***********
|
||||
Utilities
|
||||
************/
|
||||
const (
|
||||
byID = iota
|
||||
byName
|
||||
)
|
||||
|
||||
func detectNetworkTarget(vars map[string]string) (string, int) {
|
||||
if target, ok := vars[urlNwName]; ok {
|
||||
return target, byName
|
||||
}
|
||||
if target, ok := vars[urlNwID]; ok {
|
||||
return target, byID
|
||||
}
|
||||
// vars are populated from the URL, following cannot happen
|
||||
panic("Missing URL variable parameter for network")
|
||||
}
|
||||
|
||||
func detectSandboxTarget(vars map[string]string) (string, int) {
|
||||
if target, ok := vars[urlSbID]; ok {
|
||||
return target, byID
|
||||
}
|
||||
// vars are populated from the URL, following cannot happen
|
||||
panic("Missing URL variable parameter for sandbox")
|
||||
}
|
||||
|
||||
func detectEndpointTarget(vars map[string]string) (string, int) {
|
||||
if target, ok := vars[urlEpName]; ok {
|
||||
return target, byName
|
||||
}
|
||||
if target, ok := vars[urlEpID]; ok {
|
||||
return target, byID
|
||||
}
|
||||
// vars are populated from the URL, following cannot happen
|
||||
panic("Missing URL variable parameter for endpoint")
|
||||
}
|
||||
|
||||
func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) {
|
||||
var (
|
||||
nw libnetwork.Network
|
||||
err error
|
||||
)
|
||||
switch by {
|
||||
case byID:
|
||||
nw, err = c.NetworkByID(s)
|
||||
case byName:
|
||||
if s == "" {
|
||||
s = c.Config().Daemon.DefaultNetwork
|
||||
}
|
||||
nw, err = c.NetworkByName(s)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected selector for network search: %d", by))
|
||||
}
|
||||
if err != nil {
|
||||
if _, ok := err.(types.NotFoundError); ok {
|
||||
return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
|
||||
}
|
||||
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
return nw, &successResponse
|
||||
}
|
||||
|
||||
func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) {
|
||||
var (
|
||||
sb libnetwork.Sandbox
|
||||
err error
|
||||
)
|
||||
|
||||
switch by {
|
||||
case byID:
|
||||
sb, err = c.SandboxByID(s)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by))
|
||||
}
|
||||
if err != nil {
|
||||
if _, ok := err.(types.NotFoundError); ok {
|
||||
return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound}
|
||||
}
|
||||
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
return sb, &successResponse
|
||||
}
|
||||
|
||||
func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) {
|
||||
nw, errRsp := findNetwork(c, ns, nwBy)
|
||||
if !errRsp.isOK() {
|
||||
return nil, errRsp
|
||||
}
|
||||
var (
|
||||
err error
|
||||
ep libnetwork.Endpoint
|
||||
)
|
||||
switch epBy {
|
||||
case byID:
|
||||
ep, err = nw.EndpointByID(es)
|
||||
case byName:
|
||||
ep, err = nw.EndpointByName(es)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
|
||||
}
|
||||
if err != nil {
|
||||
if _, ok := err.(types.NotFoundError); ok {
|
||||
return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
|
||||
}
|
||||
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
|
||||
}
|
||||
return ep, &successResponse
|
||||
}
|
||||
|
||||
func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) {
|
||||
for _, nw := range c.Networks() {
|
||||
var (
|
||||
ep libnetwork.Endpoint
|
||||
err error
|
||||
)
|
||||
switch svBy {
|
||||
case byID:
|
||||
ep, err = nw.EndpointByID(svs)
|
||||
case byName:
|
||||
ep, err = nw.EndpointByName(svs)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected selector for service search: %d", svBy))
|
||||
}
|
||||
if err == nil {
|
||||
return ep, &successResponse
|
||||
} else if _, ok := err.(types.NotFoundError); !ok {
|
||||
return nil, convertNetworkError(err)
|
||||
}
|
||||
}
|
||||
return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound}
|
||||
}
|
||||
|
||||
func endpointToService(rsp *responseStatus) *responseStatus {
|
||||
rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1)
|
||||
return rsp
|
||||
}
|
||||
|
||||
func convertNetworkError(err error) *responseStatus {
|
||||
var code int
|
||||
switch err.(type) {
|
||||
case types.BadRequestError:
|
||||
code = http.StatusBadRequest
|
||||
case types.ForbiddenError:
|
||||
code = http.StatusForbidden
|
||||
case types.NotFoundError:
|
||||
code = http.StatusNotFound
|
||||
case types.TimeoutError:
|
||||
code = http.StatusRequestTimeout
|
||||
case types.NotImplementedError:
|
||||
code = http.StatusNotImplemented
|
||||
case types.NoServiceError:
|
||||
code = http.StatusServiceUnavailable
|
||||
case types.InternalError:
|
||||
code = http.StatusInternalServerError
|
||||
default:
|
||||
code = http.StatusInternalServerError
|
||||
}
|
||||
return &responseStatus{Status: err.Error(), StatusCode: code}
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
return json.NewEncoder(w).Encode(v)
|
||||
}
|
2404
libnetwork/api/api_linux_test.go
Normal file
2404
libnetwork/api/api_linux_test.go
Normal file
File diff suppressed because it is too large
Load diff
96
libnetwork/api/types.go
Normal file
96
libnetwork/api/types.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package api
|
||||
|
||||
import "github.com/docker/docker/libnetwork/types"
|
||||
|
||||
/***********
|
||||
Resources
|
||||
************/
|
||||
|
||||
// networkResource is the body of the "get network" http response message
|
||||
type networkResource struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Endpoints []*endpointResource `json:"endpoints"`
|
||||
}
|
||||
|
||||
// endpointResource is the body of the "get endpoint" http response message
|
||||
type endpointResource struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
// sandboxResource is the body of "get service backend" response message
|
||||
type sandboxResource struct {
|
||||
ID string `json:"id"`
|
||||
Key string `json:"key"`
|
||||
ContainerID string `json:"container_id"`
|
||||
}
|
||||
|
||||
/***********
|
||||
Body types
|
||||
************/
|
||||
|
||||
type ipamConf struct {
|
||||
PreferredPool string
|
||||
SubPool string
|
||||
Gateway string
|
||||
AuxAddresses map[string]string
|
||||
}
|
||||
|
||||
// networkCreate is the expected body of the "create network" http request message
|
||||
type networkCreate struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
NetworkType string `json:"network_type"`
|
||||
IPv4Conf []ipamConf `json:"ipv4_configuration"`
|
||||
DriverOpts map[string]string `json:"driver_opts"`
|
||||
NetworkOpts map[string]string `json:"network_opts"`
|
||||
}
|
||||
|
||||
// endpointCreate represents the body of the "create endpoint" http request message
|
||||
type endpointCreate struct {
|
||||
Name string `json:"name"`
|
||||
MyAliases []string `json:"my_aliases"`
|
||||
}
|
||||
|
||||
// sandboxCreate is the expected body of the "create sandbox" http request message
|
||||
type sandboxCreate struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
HostName string `json:"host_name"`
|
||||
DomainName string `json:"domain_name"`
|
||||
HostsPath string `json:"hosts_path"`
|
||||
ResolvConfPath string `json:"resolv_conf_path"`
|
||||
DNS []string `json:"dns"`
|
||||
ExtraHosts []extraHost `json:"extra_hosts"`
|
||||
UseDefaultSandbox bool `json:"use_default_sandbox"`
|
||||
UseExternalKey bool `json:"use_external_key"`
|
||||
ExposedPorts []types.TransportPort `json:"exposed_ports"`
|
||||
PortMapping []types.PortBinding `json:"port_mapping"`
|
||||
}
|
||||
|
||||
// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
|
||||
type endpointJoin struct {
|
||||
SandboxID string `json:"sandbox_id"`
|
||||
Aliases []string `json:"aliases"`
|
||||
}
|
||||
|
||||
// servicePublish represents the body of the "publish service" http request message
|
||||
type servicePublish struct {
|
||||
Name string `json:"name"`
|
||||
MyAliases []string `json:"my_aliases"`
|
||||
Network string `json:"network_name"`
|
||||
}
|
||||
|
||||
// serviceDelete represents the body of the "unpublish service" http request message
|
||||
type serviceDelete struct {
|
||||
Name string `json:"name"`
|
||||
Force bool `json:"force"`
|
||||
}
|
||||
|
||||
// extraHost represents the extra host object
|
||||
type extraHost struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
}
|
|
@ -10,8 +10,8 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -144,11 +144,7 @@ func (s *sequence) equal(o *sequence) bool {
|
|||
this = this.next
|
||||
other = other.next
|
||||
}
|
||||
// Check if other is longer than this
|
||||
if other != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return other == nil
|
||||
}
|
||||
|
||||
// ToByteArray converts the sequence into a byte array
|
||||
|
@ -699,7 +695,6 @@ func removeCurrentIfEmpty(head **sequence, previous, current *sequence) {
|
|||
*head = current.next
|
||||
} else {
|
||||
previous.next = current.next
|
||||
current = current.next
|
||||
}
|
||||
}
|
||||
}
|
1363
libnetwork/bitseq/sequence_test.go
Normal file
1363
libnetwork/bitseq/sequence_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,10 +2,9 @@ package bitseq
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// Key provides the Key to be used in KV Store
|
||||
|
@ -106,17 +105,6 @@ func (h *Handle) DataScope() string {
|
|||
return h.store.Scope()
|
||||
}
|
||||
|
||||
func (h *Handle) fromDsValue(value []byte) error {
|
||||
var ba []byte
|
||||
if err := json.Unmarshal(value, &ba); err != nil {
|
||||
return fmt.Errorf("failed to decode json: %s", err.Error())
|
||||
}
|
||||
if err := h.FromByteArray(ba); err != nil {
|
||||
return fmt.Errorf("failed to decode handle: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handle) writeToStore() error {
|
||||
h.Lock()
|
||||
store := h.store
|
115
libnetwork/client/client.go
Normal file
115
libnetwork/client/client.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
flag "github.com/docker/docker/libnetwork/client/mflag"
|
||||
)
|
||||
|
||||
// CallFunc provides environment specific call utility to invoke backend functions from UI
|
||||
type CallFunc func(string, string, interface{}, map[string][]string) (io.ReadCloser, http.Header, int, error)
|
||||
|
||||
// NetworkCli is the UI object for network subcmds
|
||||
type NetworkCli struct {
|
||||
out io.Writer
|
||||
err io.Writer
|
||||
call CallFunc
|
||||
}
|
||||
|
||||
// NewNetworkCli is a convenient function to create a NetworkCli object
|
||||
func NewNetworkCli(out, err io.Writer, call CallFunc) *NetworkCli {
|
||||
return &NetworkCli{
|
||||
out: out,
|
||||
err: err,
|
||||
call: call,
|
||||
}
|
||||
}
|
||||
|
||||
// getMethod is Borrowed from Docker UI which uses reflection to identify the UI Handler
|
||||
func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error, bool) {
|
||||
camelArgs := make([]string, len(args))
|
||||
for i, s := range args {
|
||||
if len(s) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
|
||||
}
|
||||
methodName := "Cmd" + strings.Join(camelArgs, "")
|
||||
method := reflect.ValueOf(cli).MethodByName(methodName)
|
||||
if !method.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
return method.Interface().(func(string, ...string) error), true
|
||||
}
|
||||
|
||||
// Cmd is borrowed from Docker UI and acts as the entry point for network UI commands.
|
||||
// network UI commands are designed to be invoked from multiple parent chains
|
||||
func (cli *NetworkCli) Cmd(chain string, args ...string) error {
|
||||
if len(args) > 2 {
|
||||
method, exists := cli.getMethod(args[:3]...)
|
||||
if exists {
|
||||
return method(chain+" "+args[0]+" "+args[1], args[3:]...)
|
||||
}
|
||||
}
|
||||
if len(args) > 1 {
|
||||
method, exists := cli.getMethod(args[:2]...)
|
||||
if exists {
|
||||
return method(chain+" "+args[0], args[2:]...)
|
||||
}
|
||||
}
|
||||
if len(args) > 0 {
|
||||
method, exists := cli.getMethod(args[0])
|
||||
if !exists {
|
||||
return fmt.Errorf("%s: '%s' is not a %s command. See '%s --help'", chain, args[0], chain, chain)
|
||||
}
|
||||
return method(chain, args[1:]...)
|
||||
}
|
||||
flag.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subcmd is borrowed from Docker UI and performs the same function of configuring the subCmds
|
||||
func (cli *NetworkCli) Subcmd(chain, name, signature, description string, exitOnError bool) *flag.FlagSet {
|
||||
var errorHandling flag.ErrorHandling
|
||||
if exitOnError {
|
||||
errorHandling = flag.ExitOnError
|
||||
} else {
|
||||
errorHandling = flag.ContinueOnError
|
||||
}
|
||||
flags := flag.NewFlagSet(name, errorHandling)
|
||||
flags.Usage = func() {
|
||||
flags.ShortUsage()
|
||||
flags.PrintDefaults()
|
||||
}
|
||||
flags.ShortUsage = func() {
|
||||
options := ""
|
||||
if signature != "" {
|
||||
signature = " " + signature
|
||||
}
|
||||
if flags.FlagCountUndeprecated() > 0 {
|
||||
options = " [OPTIONS]"
|
||||
}
|
||||
fmt.Fprintf(cli.out, "\nUsage: %s %s%s%s\n\n%s\n\n", chain, name, options, signature, description)
|
||||
flags.SetOutput(cli.out)
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func readBody(stream io.ReadCloser, hdr http.Header, statusCode int, err error) ([]byte, int, error) {
|
||||
if stream != nil {
|
||||
defer stream.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, statusCode, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(stream)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
return body, statusCode, nil
|
||||
}
|
122
libnetwork/client/client_service_test.go
Normal file
122
libnetwork/client/client_service_test.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
_ "github.com/docker/docker/libnetwork/testutils"
|
||||
)
|
||||
|
||||
func TestClientServiceInvalidCommand(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "invalid")
|
||||
if err == nil {
|
||||
t.Fatal("Passing invalid commands must fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceCreate(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "publish", mockServiceName+"."+mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceRm(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "unpublish", mockServiceName+"."+mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceLs(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "ls")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceInfo(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "info", mockServiceName+"."+mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceInfoById(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "info", mockServiceID+"."+mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceJoin(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "attach", mockContainerID, mockServiceName+"."+mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientServiceLeave(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "service", "detach", mockContainerID, mockServiceName+"."+mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help
|
||||
// TODO : Handle the --help test-case in the IT when CLI is available
|
||||
/*
|
||||
func TestClientNetworkServiceCreateHelp(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "create", "--help")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case.
|
||||
// TODO : Handle the missing argument case in the IT when CLI is available
|
||||
/*
|
||||
func TestClientNetworkServiceCreateMissingArgument(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "create")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
*/
|
228
libnetwork/client/client_test.go
Normal file
228
libnetwork/client/client_test.go
Normal file
|
@ -0,0 +1,228 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/docker/docker/libnetwork/testutils"
|
||||
)
|
||||
|
||||
// nopCloser is used to provide a dummy CallFunc for Cmd()
|
||||
type nopCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setupMockHTTPCallback()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
var callbackFunc func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error)
|
||||
var mockNwJSON, mockNwListJSON, mockServiceJSON, mockServiceListJSON, mockSbJSON, mockSbListJSON []byte
|
||||
var mockNwName = "test"
|
||||
var mockNwID = "2a3456789"
|
||||
var mockServiceName = "testSrv"
|
||||
var mockServiceID = "2a3456789"
|
||||
var mockContainerID = "2a3456789"
|
||||
var mockSandboxID = "2b3456789"
|
||||
|
||||
func setupMockHTTPCallback() {
|
||||
var list []networkResource
|
||||
nw := networkResource{Name: mockNwName, ID: mockNwID}
|
||||
mockNwJSON, _ = json.Marshal(nw)
|
||||
list = append(list, nw)
|
||||
mockNwListJSON, _ = json.Marshal(list)
|
||||
|
||||
var srvList []serviceResource
|
||||
ep := serviceResource{Name: mockServiceName, ID: mockServiceID, Network: mockNwName}
|
||||
mockServiceJSON, _ = json.Marshal(ep)
|
||||
srvList = append(srvList, ep)
|
||||
mockServiceListJSON, _ = json.Marshal(srvList)
|
||||
|
||||
var sbxList []SandboxResource
|
||||
sb := SandboxResource{ID: mockSandboxID, ContainerID: mockContainerID}
|
||||
mockSbJSON, _ = json.Marshal(sb)
|
||||
sbxList = append(sbxList, sb)
|
||||
mockSbListJSON, _ = json.Marshal(sbxList)
|
||||
|
||||
dummyHTTPHdr := http.Header{}
|
||||
|
||||
callbackFunc = func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
|
||||
var rsp string
|
||||
switch method {
|
||||
case "GET":
|
||||
if strings.Contains(path, fmt.Sprintf("networks?name=%s", mockNwName)) {
|
||||
rsp = string(mockNwListJSON)
|
||||
} else if strings.Contains(path, "networks?name=") {
|
||||
rsp = "[]"
|
||||
} else if strings.Contains(path, fmt.Sprintf("networks?partial-id=%s", mockNwID)) {
|
||||
rsp = string(mockNwListJSON)
|
||||
} else if strings.Contains(path, "networks?partial-id=") {
|
||||
rsp = "[]"
|
||||
} else if strings.HasSuffix(path, "networks") {
|
||||
rsp = string(mockNwListJSON)
|
||||
} else if strings.HasSuffix(path, "networks/"+mockNwID) {
|
||||
rsp = string(mockNwJSON)
|
||||
} else if strings.Contains(path, fmt.Sprintf("services?name=%s", mockServiceName)) {
|
||||
rsp = string(mockServiceListJSON)
|
||||
} else if strings.Contains(path, "services?name=") {
|
||||
rsp = "[]"
|
||||
} else if strings.Contains(path, fmt.Sprintf("services?partial-id=%s", mockServiceID)) {
|
||||
rsp = string(mockServiceListJSON)
|
||||
} else if strings.Contains(path, "services?partial-id=") {
|
||||
rsp = "[]"
|
||||
} else if strings.HasSuffix(path, "services") {
|
||||
rsp = string(mockServiceListJSON)
|
||||
} else if strings.HasSuffix(path, "services/"+mockServiceID) {
|
||||
rsp = string(mockServiceJSON)
|
||||
} else if strings.Contains(path, "containers") {
|
||||
return nopCloser{bytes.NewBufferString("")}, dummyHTTPHdr, 400, fmt.Errorf("Bad Request")
|
||||
} else if strings.Contains(path, fmt.Sprintf("sandboxes?container-id=%s", mockContainerID)) {
|
||||
rsp = string(mockSbListJSON)
|
||||
} else if strings.Contains(path, fmt.Sprintf("sandboxes?partial-container-id=%s", mockContainerID)) {
|
||||
rsp = string(mockSbListJSON)
|
||||
}
|
||||
case "POST":
|
||||
var data []byte
|
||||
if strings.HasSuffix(path, "networks") {
|
||||
data, _ = json.Marshal(mockNwID)
|
||||
} else if strings.HasSuffix(path, "services") {
|
||||
data, _ = json.Marshal(mockServiceID)
|
||||
} else if strings.HasSuffix(path, "backend") {
|
||||
data, _ = json.Marshal(mockSandboxID)
|
||||
}
|
||||
rsp = string(data)
|
||||
case "PUT":
|
||||
case "DELETE":
|
||||
rsp = ""
|
||||
}
|
||||
return nopCloser{bytes.NewBufferString(rsp)}, dummyHTTPHdr, 200, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDummyCommand(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "dummy")
|
||||
if err == nil {
|
||||
t.Fatal("Incorrect Command must fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkInvalidCommand(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "invalid")
|
||||
if err == nil {
|
||||
t.Fatal("Passing invalid commands must fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkCreate(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "create", mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkCreateWithDriver(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "create", "-f=dummy", mockNwName)
|
||||
if err == nil {
|
||||
t.Fatal("Passing incorrect flags to the create command must fail")
|
||||
}
|
||||
|
||||
err = cli.Cmd("docker", "network", "create", "-d=dummy", mockNwName)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkRm(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "rm", mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkLs(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "ls")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkInfo(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "info", mockNwName)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkInfoById(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "info", mockNwID)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help
|
||||
// TODO : Handle the --help test-case in the IT when CLI is available
|
||||
/*
|
||||
func TestClientNetworkServiceCreateHelp(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "create", "--help")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case.
|
||||
// TODO : Handle the missing argument case in the IT when CLI is available
|
||||
/*
|
||||
func TestClientNetworkServiceCreateMissingArgument(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "create")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
*/
|
27
libnetwork/client/mflag/LICENSE
Normal file
27
libnetwork/client/mflag/LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2014-2016 The Docker & Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
40
libnetwork/client/mflag/README.md
Normal file
40
libnetwork/client/mflag/README.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
Package mflag (aka multiple-flag) implements command-line flag parsing.
|
||||
It's an **hacky** fork of the [official golang package](http://golang.org/pkg/flag/)
|
||||
|
||||
It adds:
|
||||
|
||||
* both short and long flag version
|
||||
`./example -s red` `./example --string blue`
|
||||
|
||||
* multiple names for the same option
|
||||
```
|
||||
$>./example -h
|
||||
Usage of example:
|
||||
-s, --string="": a simple string
|
||||
```
|
||||
|
||||
___
|
||||
It is very flexible on purpose, so you can do things like:
|
||||
```
|
||||
$>./example -h
|
||||
Usage of example:
|
||||
-s, -string, --string="": a simple string
|
||||
```
|
||||
|
||||
Or:
|
||||
```
|
||||
$>./example -h
|
||||
Usage of example:
|
||||
-oldflag, --newflag="": a simple string
|
||||
```
|
||||
|
||||
You can also hide some flags from the usage, so if we want only `--newflag`:
|
||||
```
|
||||
$>./example -h
|
||||
Usage of example:
|
||||
--newflag="": a simple string
|
||||
$>./example -oldflag str
|
||||
str
|
||||
```
|
||||
|
||||
See [example.go](example/example.go) for more details.
|
36
libnetwork/client/mflag/example/example.go
Normal file
36
libnetwork/client/mflag/example/example.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
flag "github.com/docker/docker/libnetwork/client/mflag"
|
||||
)
|
||||
|
||||
var (
|
||||
i int
|
||||
str string
|
||||
b, b2, h bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Bool([]string{"#hp", "#-help"}, false, "display the help")
|
||||
flag.BoolVar(&b, []string{"b", "#bal", "#bol", "-bal"}, false, "a simple bool")
|
||||
flag.BoolVar(&b, []string{"g", "#gil"}, false, "a simple bool")
|
||||
flag.BoolVar(&b2, []string{"#-bool"}, false, "a simple bool")
|
||||
flag.IntVar(&i, []string{"-integer", "-number"}, -1, "a simple integer")
|
||||
flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage
|
||||
flag.BoolVar(&h, []string{"h", "#help", "-help"}, false, "display the help")
|
||||
flag.StringVar(&str, []string{"mode"}, "mode1", "set the mode\nmode1: use the mode1\nmode2: use the mode2\nmode3: use the mode3")
|
||||
flag.Parse()
|
||||
}
|
||||
func main() {
|
||||
if h {
|
||||
flag.PrintDefaults()
|
||||
} else {
|
||||
fmt.Printf("s/#hidden/-string: %s\n", str)
|
||||
fmt.Printf("b: %t\n", b)
|
||||
fmt.Printf("-bool: %t\n", b2)
|
||||
fmt.Printf("s/#hidden/-string(via lookup): %s\n", flag.Lookup("s").Value.String())
|
||||
fmt.Printf("ARGS: %v\n", flag.Args())
|
||||
}
|
||||
}
|
1326
libnetwork/client/mflag/flag.go
Normal file
1326
libnetwork/client/mflag/flag.go
Normal file
File diff suppressed because it is too large
Load diff
527
libnetwork/client/mflag/flag_test.go
Normal file
527
libnetwork/client/mflag/flag_test.go
Normal file
|
@ -0,0 +1,527 @@
|
|||
// Copyright 2014-2016 The Docker & Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mflag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/docker/docker/libnetwork/testutils"
|
||||
)
|
||||
|
||||
// ResetForTesting clears all flag state and sets the usage function as directed.
|
||||
// After calling ResetForTesting, parse errors in flag handling will not
|
||||
// exit the program.
|
||||
func ResetForTesting(usage func()) {
|
||||
CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
|
||||
Usage = usage
|
||||
}
|
||||
func boolString(s string) string {
|
||||
if s == "0" {
|
||||
return "false"
|
||||
}
|
||||
return "true"
|
||||
}
|
||||
|
||||
func TestEverything(t *testing.T) {
|
||||
ResetForTesting(nil)
|
||||
Bool([]string{"test_bool"}, false, "bool value")
|
||||
Int([]string{"test_int"}, 0, "int value")
|
||||
Int64([]string{"test_int64"}, 0, "int64 value")
|
||||
Uint([]string{"test_uint"}, 0, "uint value")
|
||||
Uint64([]string{"test_uint64"}, 0, "uint64 value")
|
||||
String([]string{"test_string"}, "0", "string value")
|
||||
Float64([]string{"test_float64"}, 0, "float64 value")
|
||||
Duration([]string{"test_duration"}, 0, "time.Duration value")
|
||||
|
||||
m := make(map[string]*Flag)
|
||||
desired := "0"
|
||||
visitor := func(f *Flag) {
|
||||
for _, name := range f.Names {
|
||||
if len(name) > 5 && name[0:5] == "test_" {
|
||||
m[name] = f
|
||||
ok := false
|
||||
switch {
|
||||
case f.Value.String() == desired:
|
||||
ok = true
|
||||
case name == "test_bool" && f.Value.String() == boolString(desired):
|
||||
ok = true
|
||||
case name == "test_duration" && f.Value.String() == desired+"s":
|
||||
ok = true
|
||||
}
|
||||
if !ok {
|
||||
t.Error("Visit: bad value", f.Value.String(), "for", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VisitAll(visitor)
|
||||
if len(m) != 8 {
|
||||
t.Error("VisitAll misses some flags")
|
||||
for k, v := range m {
|
||||
t.Log(k, *v)
|
||||
}
|
||||
}
|
||||
m = make(map[string]*Flag)
|
||||
Visit(visitor)
|
||||
if len(m) != 0 {
|
||||
t.Error("Visit sees unset flags")
|
||||
for k, v := range m {
|
||||
t.Log(k, *v)
|
||||
}
|
||||
}
|
||||
// Now set all flags
|
||||
Set("test_bool", "true")
|
||||
Set("test_int", "1")
|
||||
Set("test_int64", "1")
|
||||
Set("test_uint", "1")
|
||||
Set("test_uint64", "1")
|
||||
Set("test_string", "1")
|
||||
Set("test_float64", "1")
|
||||
Set("test_duration", "1s")
|
||||
desired = "1"
|
||||
Visit(visitor)
|
||||
if len(m) != 8 {
|
||||
t.Error("Visit fails after set")
|
||||
for k, v := range m {
|
||||
t.Log(k, *v)
|
||||
}
|
||||
}
|
||||
// Now test they're visited in sort order.
|
||||
var flagNames []string
|
||||
Visit(func(f *Flag) {
|
||||
flagNames = append(flagNames, f.Names...)
|
||||
})
|
||||
if !sort.StringsAreSorted(flagNames) {
|
||||
t.Errorf("flag names not sorted: %v", flagNames)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
ResetForTesting(nil)
|
||||
Bool([]string{"test_bool"}, true, "bool value")
|
||||
Int([]string{"test_int"}, 1, "int value")
|
||||
Int64([]string{"test_int64"}, 2, "int64 value")
|
||||
Uint([]string{"test_uint"}, 3, "uint value")
|
||||
Uint64([]string{"test_uint64"}, 4, "uint64 value")
|
||||
String([]string{"test_string"}, "5", "string value")
|
||||
Float64([]string{"test_float64"}, 6, "float64 value")
|
||||
Duration([]string{"test_duration"}, 7, "time.Duration value")
|
||||
|
||||
visitor := func(f *Flag) {
|
||||
for _, name := range f.Names {
|
||||
if len(name) > 5 && name[0:5] == "test_" {
|
||||
g, ok := f.Value.(Getter)
|
||||
if !ok {
|
||||
t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
|
||||
return
|
||||
}
|
||||
switch name {
|
||||
case "test_bool":
|
||||
ok = g.Get() == true
|
||||
case "test_int":
|
||||
ok = g.Get() == int(1)
|
||||
case "test_int64":
|
||||
ok = g.Get() == int64(2)
|
||||
case "test_uint":
|
||||
ok = g.Get() == uint(3)
|
||||
case "test_uint64":
|
||||
ok = g.Get() == uint64(4)
|
||||
case "test_string":
|
||||
ok = g.Get() == "5"
|
||||
case "test_float64":
|
||||
ok = g.Get() == float64(6)
|
||||
case "test_duration":
|
||||
ok = g.Get() == time.Duration(7)
|
||||
}
|
||||
if !ok {
|
||||
t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VisitAll(visitor)
|
||||
}
|
||||
|
||||
func testParse(f *FlagSet, t *testing.T) {
|
||||
if f.Parsed() {
|
||||
t.Error("f.Parse() = true before Parse")
|
||||
}
|
||||
boolFlag := f.Bool([]string{"bool"}, false, "bool value")
|
||||
bool2Flag := f.Bool([]string{"bool2"}, false, "bool2 value")
|
||||
f.Bool([]string{"bool3"}, false, "bool3 value")
|
||||
bool4Flag := f.Bool([]string{"bool4"}, false, "bool4 value")
|
||||
intFlag := f.Int([]string{"-int"}, 0, "int value")
|
||||
int64Flag := f.Int64([]string{"-int64"}, 0, "int64 value")
|
||||
uintFlag := f.Uint([]string{"uint"}, 0, "uint value")
|
||||
uint64Flag := f.Uint64([]string{"-uint64"}, 0, "uint64 value")
|
||||
stringFlag := f.String([]string{"string"}, "0", "string value")
|
||||
f.String([]string{"string2"}, "0", "string2 value")
|
||||
singleQuoteFlag := f.String([]string{"squote"}, "", "single quoted value")
|
||||
doubleQuoteFlag := f.String([]string{"dquote"}, "", "double quoted value")
|
||||
mixedQuoteFlag := f.String([]string{"mquote"}, "", "mixed quoted value")
|
||||
mixed2QuoteFlag := f.String([]string{"mquote2"}, "", "mixed2 quoted value")
|
||||
nestedQuoteFlag := f.String([]string{"nquote"}, "", "nested quoted value")
|
||||
nested2QuoteFlag := f.String([]string{"nquote2"}, "", "nested2 quoted value")
|
||||
float64Flag := f.Float64([]string{"float64"}, 0, "float64 value")
|
||||
durationFlag := f.Duration([]string{"duration"}, 5*time.Second, "time.Duration value")
|
||||
extra := "one-extra-argument"
|
||||
args := []string{
|
||||
"-bool",
|
||||
"-bool2=true",
|
||||
"-bool4=false",
|
||||
"--int", "22",
|
||||
"--int64", "0x23",
|
||||
"-uint", "24",
|
||||
"--uint64", "25",
|
||||
"-string", "hello",
|
||||
"-squote='single'",
|
||||
`-dquote="double"`,
|
||||
`-mquote='mixed"`,
|
||||
`-mquote2="mixed2'`,
|
||||
`-nquote="'single nested'"`,
|
||||
`-nquote2='"double nested"'`,
|
||||
"-float64", "2718e28",
|
||||
"-duration", "2m",
|
||||
extra,
|
||||
}
|
||||
if err := f.Parse(args); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !f.Parsed() {
|
||||
t.Error("f.Parse() = false after Parse")
|
||||
}
|
||||
if *boolFlag != true {
|
||||
t.Error("bool flag should be true, is ", *boolFlag)
|
||||
}
|
||||
if *bool2Flag != true {
|
||||
t.Error("bool2 flag should be true, is ", *bool2Flag)
|
||||
}
|
||||
if !f.IsSet("bool2") {
|
||||
t.Error("bool2 should be marked as set")
|
||||
}
|
||||
if f.IsSet("bool3") {
|
||||
t.Error("bool3 should not be marked as set")
|
||||
}
|
||||
if !f.IsSet("bool4") {
|
||||
t.Error("bool4 should be marked as set")
|
||||
}
|
||||
if *bool4Flag != false {
|
||||
t.Error("bool4 flag should be false, is ", *bool4Flag)
|
||||
}
|
||||
if *intFlag != 22 {
|
||||
t.Error("int flag should be 22, is ", *intFlag)
|
||||
}
|
||||
if *int64Flag != 0x23 {
|
||||
t.Error("int64 flag should be 0x23, is ", *int64Flag)
|
||||
}
|
||||
if *uintFlag != 24 {
|
||||
t.Error("uint flag should be 24, is ", *uintFlag)
|
||||
}
|
||||
if *uint64Flag != 25 {
|
||||
t.Error("uint64 flag should be 25, is ", *uint64Flag)
|
||||
}
|
||||
if *stringFlag != "hello" {
|
||||
t.Error("string flag should be `hello`, is ", *stringFlag)
|
||||
}
|
||||
if !f.IsSet("string") {
|
||||
t.Error("string flag should be marked as set")
|
||||
}
|
||||
if f.IsSet("string2") {
|
||||
t.Error("string2 flag should not be marked as set")
|
||||
}
|
||||
if *singleQuoteFlag != "single" {
|
||||
t.Error("single quote string flag should be `single`, is ", *singleQuoteFlag)
|
||||
}
|
||||
if *doubleQuoteFlag != "double" {
|
||||
t.Error("double quote string flag should be `double`, is ", *doubleQuoteFlag)
|
||||
}
|
||||
if *mixedQuoteFlag != `'mixed"` {
|
||||
t.Error("mixed quote string flag should be `'mixed\"`, is ", *mixedQuoteFlag)
|
||||
}
|
||||
if *mixed2QuoteFlag != `"mixed2'` {
|
||||
t.Error("mixed2 quote string flag should be `\"mixed2'`, is ", *mixed2QuoteFlag)
|
||||
}
|
||||
if *nestedQuoteFlag != "'single nested'" {
|
||||
t.Error("nested quote string flag should be `'single nested'`, is ", *nestedQuoteFlag)
|
||||
}
|
||||
if *nested2QuoteFlag != `"double nested"` {
|
||||
t.Error("double quote string flag should be `\"double nested\"`, is ", *nested2QuoteFlag)
|
||||
}
|
||||
if *float64Flag != 2718e28 {
|
||||
t.Error("float64 flag should be 2718e28, is ", *float64Flag)
|
||||
}
|
||||
if *durationFlag != 2*time.Minute {
|
||||
t.Error("duration flag should be 2m, is ", *durationFlag)
|
||||
}
|
||||
if len(f.Args()) != 1 {
|
||||
t.Error("expected one argument, got", len(f.Args()))
|
||||
} else if f.Args()[0] != extra {
|
||||
t.Errorf("expected argument %q got %q", extra, f.Args()[0])
|
||||
}
|
||||
}
|
||||
|
||||
func testPanic(f *FlagSet, t *testing.T) {
|
||||
f.Int([]string{"-int"}, 0, "int value")
|
||||
if f.Parsed() {
|
||||
t.Error("f.Parse() = true before Parse")
|
||||
}
|
||||
args := []string{
|
||||
"-int", "21",
|
||||
}
|
||||
f.Parse(args)
|
||||
}
|
||||
|
||||
func TestParsePanic(t *testing.T) {
|
||||
ResetForTesting(func() {})
|
||||
testPanic(CommandLine, t)
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
ResetForTesting(func() { t.Error("bad parse") })
|
||||
testParse(CommandLine, t)
|
||||
}
|
||||
|
||||
func TestFlagSetParse(t *testing.T) {
|
||||
testParse(NewFlagSet("test", ContinueOnError), t)
|
||||
}
|
||||
|
||||
// Declare a user-defined flag type.
|
||||
type flagVar []string
|
||||
|
||||
func (f *flagVar) String() string {
|
||||
return fmt.Sprint([]string(*f))
|
||||
}
|
||||
|
||||
func (f *flagVar) Set(value string) error {
|
||||
*f = append(*f, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUserDefined(t *testing.T) {
|
||||
var flags FlagSet
|
||||
flags.Init("test", ContinueOnError)
|
||||
var v flagVar
|
||||
flags.Var(&v, []string{"v"}, "usage")
|
||||
if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(v) != 3 {
|
||||
t.Fatal("expected 3 args; got ", len(v))
|
||||
}
|
||||
expect := "[1 2 3]"
|
||||
if v.String() != expect {
|
||||
t.Errorf("expected value %q got %q", expect, v.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Declare a user-defined boolean flag type.
|
||||
type boolFlagVar struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (b *boolFlagVar) String() string {
|
||||
return fmt.Sprintf("%d", b.count)
|
||||
}
|
||||
|
||||
func (b *boolFlagVar) Set(value string) error {
|
||||
if value == "true" {
|
||||
b.count++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boolFlagVar) IsBoolFlag() bool {
|
||||
return b.count < 4
|
||||
}
|
||||
|
||||
func TestUserDefinedBool(t *testing.T) {
|
||||
var flags FlagSet
|
||||
flags.Init("test", ContinueOnError)
|
||||
var b boolFlagVar
|
||||
var err error
|
||||
flags.Var(&b, []string{"b"}, "usage")
|
||||
if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
|
||||
if b.count < 4 {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if b.count != 4 {
|
||||
t.Errorf("want: %d; got: %d", 4, b.count)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
t.Error("expected error; got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetOutput(t *testing.T) {
|
||||
var flags FlagSet
|
||||
var buf bytes.Buffer
|
||||
flags.SetOutput(&buf)
|
||||
flags.Init("test", ContinueOnError)
|
||||
flags.Parse([]string{"-unknown"})
|
||||
if out := buf.String(); !strings.Contains(out, "-unknown") {
|
||||
t.Logf("expected output mentioning unknown; got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
// This tests that one can reset the flags. This still works but not well, and is
|
||||
// superseded by FlagSet.
|
||||
func TestChangingArgs(t *testing.T) {
|
||||
ResetForTesting(func() { t.Fatal("bad parse") })
|
||||
oldArgs := os.Args
|
||||
defer func() { os.Args = oldArgs }()
|
||||
os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
|
||||
before := Bool([]string{"before"}, false, "")
|
||||
if err := CommandLine.Parse(os.Args[1:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cmd := Arg(0)
|
||||
os.Args = Args()
|
||||
after := Bool([]string{"after"}, false, "")
|
||||
Parse()
|
||||
args := Args()
|
||||
|
||||
if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
|
||||
t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that -help invokes the usage message and returns ErrHelp.
|
||||
func TestHelp(t *testing.T) {
|
||||
var helpCalled = false
|
||||
fs := NewFlagSet("help test", ContinueOnError)
|
||||
fs.Usage = func() { helpCalled = true }
|
||||
var flag bool
|
||||
fs.BoolVar(&flag, []string{"flag"}, false, "regular flag")
|
||||
// Regular flag invocation should work
|
||||
err := fs.Parse([]string{"-flag=true"})
|
||||
if err != nil {
|
||||
t.Fatal("expected no error; got ", err)
|
||||
}
|
||||
if !flag {
|
||||
t.Error("flag was not set by -flag")
|
||||
}
|
||||
if helpCalled {
|
||||
t.Error("help called for regular flag")
|
||||
helpCalled = false // reset for next test
|
||||
}
|
||||
// Help flag should work as expected.
|
||||
err = fs.Parse([]string{"-help"})
|
||||
if err == nil {
|
||||
t.Fatal("error expected")
|
||||
}
|
||||
if err != ErrHelp {
|
||||
t.Fatal("expected ErrHelp; got ", err)
|
||||
}
|
||||
if !helpCalled {
|
||||
t.Fatal("help was not called")
|
||||
}
|
||||
// If we define a help flag, that should override.
|
||||
var help bool
|
||||
fs.BoolVar(&help, []string{"help"}, false, "help flag")
|
||||
helpCalled = false
|
||||
err = fs.Parse([]string{"-help"})
|
||||
if err != nil {
|
||||
t.Fatal("expected no error for defined -help; got ", err)
|
||||
}
|
||||
if helpCalled {
|
||||
t.Fatal("help was called; should not have been for defined help flag")
|
||||
}
|
||||
}
|
||||
|
||||
// Test the flag count functions.
|
||||
func TestFlagCounts(t *testing.T) {
|
||||
fs := NewFlagSet("help test", ContinueOnError)
|
||||
var flag bool
|
||||
fs.BoolVar(&flag, []string{"flag1"}, false, "regular flag")
|
||||
fs.BoolVar(&flag, []string{"#deprecated1"}, false, "regular flag")
|
||||
fs.BoolVar(&flag, []string{"f", "flag2"}, false, "regular flag")
|
||||
fs.BoolVar(&flag, []string{"#d", "#deprecated2"}, false, "regular flag")
|
||||
fs.BoolVar(&flag, []string{"flag3"}, false, "regular flag")
|
||||
fs.BoolVar(&flag, []string{"g", "#flag4", "-flag4"}, false, "regular flag")
|
||||
|
||||
if fs.FlagCount() != 6 {
|
||||
t.Fatal("FlagCount wrong. ", fs.FlagCount())
|
||||
}
|
||||
if fs.FlagCountUndeprecated() != 4 {
|
||||
t.Fatal("FlagCountUndeprecated wrong. ", fs.FlagCountUndeprecated())
|
||||
}
|
||||
if fs.NFlag() != 0 {
|
||||
t.Fatal("NFlag wrong. ", fs.NFlag())
|
||||
}
|
||||
err := fs.Parse([]string{"-fd", "-g", "-flag4"})
|
||||
if err != nil {
|
||||
t.Fatal("expected no error for defined -help; got ", err)
|
||||
}
|
||||
if fs.NFlag() != 4 {
|
||||
t.Fatal("NFlag wrong. ", fs.NFlag())
|
||||
}
|
||||
}
|
||||
|
||||
// Show up bug in sortFlags
|
||||
func TestSortFlags(t *testing.T) {
|
||||
fs := NewFlagSet("help TestSortFlags", ContinueOnError)
|
||||
|
||||
var err error
|
||||
|
||||
var b bool
|
||||
fs.BoolVar(&b, []string{"b", "-banana"}, false, "usage")
|
||||
|
||||
err = fs.Parse([]string{"--banana=true"})
|
||||
if err != nil {
|
||||
t.Fatal("expected no error; got ", err)
|
||||
}
|
||||
|
||||
count := 0
|
||||
|
||||
fs.VisitAll(func(flag *Flag) {
|
||||
count++
|
||||
if flag == nil {
|
||||
t.Fatal("VisitAll should not return a nil flag")
|
||||
}
|
||||
})
|
||||
flagcount := fs.FlagCount()
|
||||
if flagcount != count {
|
||||
t.Fatalf("FlagCount (%d) != number (%d) of elements visited", flagcount, count)
|
||||
}
|
||||
// Make sure its idempotent
|
||||
if flagcount != fs.FlagCount() {
|
||||
t.Fatalf("FlagCount (%d) != fs.FlagCount() (%d) of elements visited", flagcount, fs.FlagCount())
|
||||
}
|
||||
|
||||
count = 0
|
||||
fs.Visit(func(flag *Flag) {
|
||||
count++
|
||||
if flag == nil {
|
||||
t.Fatal("Visit should not return a nil flag")
|
||||
}
|
||||
})
|
||||
nflag := fs.NFlag()
|
||||
if nflag != count {
|
||||
t.Fatalf("NFlag (%d) != number (%d) of elements visited", nflag, count)
|
||||
}
|
||||
if nflag != fs.NFlag() {
|
||||
t.Fatalf("NFlag (%d) != fs.NFlag() (%d) of elements visited", nflag, fs.NFlag())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeFlags(t *testing.T) {
|
||||
base := NewFlagSet("base", ContinueOnError)
|
||||
base.String([]string{"f"}, "", "")
|
||||
|
||||
fs := NewFlagSet("test", ContinueOnError)
|
||||
Merge(fs, base)
|
||||
if len(fs.formal) != 1 {
|
||||
t.Fatalf("FlagCount (%d) != number (1) of elements merged", len(fs.formal))
|
||||
}
|
||||
}
|
267
libnetwork/client/network.go
Normal file
267
libnetwork/client/network.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
flag "github.com/docker/docker/libnetwork/client/mflag"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
var (
|
||||
networkCommands = []command{
|
||||
{"create", "Create a network"},
|
||||
{"rm", "Remove a network"},
|
||||
{"ls", "List all networks"},
|
||||
{"info", "Display information of a network"},
|
||||
}
|
||||
)
|
||||
|
||||
// CmdNetwork handles the root Network UI
|
||||
func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false)
|
||||
cmd.Require(flag.Min, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err == nil {
|
||||
cmd.Usage()
|
||||
return fmt.Errorf("invalid command : %v", args)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// CmdNetworkCreate handles Network Create UI
|
||||
func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false)
|
||||
flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network")
|
||||
flID := cmd.String([]string{"-id"}, "", "Network ID string")
|
||||
flOpts := cmd.String([]string{"o", "-opt"}, "", "Network options")
|
||||
flInternal := cmd.Bool([]string{"-internal"}, false, "Config the network to be internal")
|
||||
flIPv6 := cmd.Bool([]string{"-ipv6"}, false, "Enable IPv6 on the network")
|
||||
flSubnet := cmd.String([]string{"-subnet"}, "", "Subnet option")
|
||||
flRange := cmd.String([]string{"-ip-range"}, "", "Range option")
|
||||
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkOpts := make(map[string]string)
|
||||
if *flInternal {
|
||||
networkOpts[netlabel.Internal] = "true"
|
||||
}
|
||||
if *flIPv6 {
|
||||
networkOpts[netlabel.EnableIPv6] = "true"
|
||||
}
|
||||
|
||||
driverOpts := make(map[string]string)
|
||||
if *flOpts != "" {
|
||||
opts := strings.Split(*flOpts, ",")
|
||||
for _, opt := range opts {
|
||||
driverOpts[netlabel.Key(opt)] = netlabel.Value(opt)
|
||||
}
|
||||
}
|
||||
|
||||
var icList []ipamConf
|
||||
if *flSubnet != "" {
|
||||
ic := ipamConf{
|
||||
PreferredPool: *flSubnet,
|
||||
}
|
||||
|
||||
if *flRange != "" {
|
||||
ic.SubPool = *flRange
|
||||
}
|
||||
|
||||
icList = append(icList, ic)
|
||||
}
|
||||
|
||||
// Construct network create request body
|
||||
nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, ID: *flID, IPv4Conf: icList, DriverOpts: driverOpts, NetworkOpts: networkOpts}
|
||||
obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var replyID string
|
||||
err = json.Unmarshal(obj, &replyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s\n", replyID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdNetworkRm handles Network Delete UI
|
||||
func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := lookupNetworkID(cli, cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdNetworkLs handles Network List UI
|
||||
func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false)
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
|
||||
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
|
||||
nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
|
||||
last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *last == -1 && *nLatest {
|
||||
*last = 1
|
||||
}
|
||||
|
||||
var networkResources []networkResource
|
||||
err = json.Unmarshal(obj, &networkResources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
|
||||
// unless quiet (-q) is specified, print field titles
|
||||
if !*quiet {
|
||||
fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE")
|
||||
}
|
||||
|
||||
for _, networkResource := range networkResources {
|
||||
ID := networkResource.ID
|
||||
netName := networkResource.Name
|
||||
if !*noTrunc {
|
||||
ID = stringid.TruncateID(ID)
|
||||
}
|
||||
if *quiet {
|
||||
fmt.Fprintln(wr, ID)
|
||||
continue
|
||||
}
|
||||
netType := networkResource.Type
|
||||
fmt.Fprintf(wr, "%s\t%s\t%s\t",
|
||||
ID,
|
||||
netName,
|
||||
netType)
|
||||
fmt.Fprint(wr, "\n")
|
||||
}
|
||||
wr.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdNetworkInfo handles Network Info UI
|
||||
func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := lookupNetworkID(cli, cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkResource := &networkResource{}
|
||||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID)
|
||||
fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name)
|
||||
fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type)
|
||||
if networkResource.Services != nil {
|
||||
for _, serviceResource := range networkResource.Services {
|
||||
fmt.Fprintf(cli.out, " Service Id: %s\n", serviceResource.ID)
|
||||
fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to predict if a string is a name or id or partial-id
|
||||
// This provides a best-effort mechanism to identify an id with the help of GET Filter APIs
|
||||
// Being a UI, its most likely that name will be used by the user, which is used to lookup
|
||||
// the corresponding ID. If ID is not found, this function will assume that the passed string
|
||||
// is an ID by itself.
|
||||
|
||||
func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) {
|
||||
obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
|
||||
}
|
||||
|
||||
var list []*networkResource
|
||||
err = json.Unmarshal(obj, &list)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
// name query filter will always return a single-element collection
|
||||
return list[0].ID, nil
|
||||
}
|
||||
|
||||
// Check for Partial-id
|
||||
obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(obj, &list)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return "", fmt.Errorf("resource not found %s", nameID)
|
||||
}
|
||||
if len(list) > 1 {
|
||||
return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID)
|
||||
}
|
||||
return list[0].ID, nil
|
||||
}
|
||||
|
||||
func networkUsage(chain string) string {
|
||||
help := "Commands:\n"
|
||||
|
||||
for _, cmd := range networkCommands {
|
||||
help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description)
|
||||
}
|
||||
|
||||
help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain)
|
||||
return help
|
||||
}
|
400
libnetwork/client/service.go
Normal file
400
libnetwork/client/service.go
Normal file
|
@ -0,0 +1,400 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
flag "github.com/docker/docker/libnetwork/client/mflag"
|
||||
"github.com/docker/docker/libnetwork/netutils"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
)
|
||||
|
||||
var (
|
||||
serviceCommands = []command{
|
||||
{"publish", "Publish a service"},
|
||||
{"unpublish", "Remove a service"},
|
||||
{"attach", "Attach a backend (container) to the service"},
|
||||
{"detach", "Detach the backend from the service"},
|
||||
{"ls", "Lists all services"},
|
||||
{"info", "Display information about a service"},
|
||||
}
|
||||
)
|
||||
|
||||
func lookupServiceID(cli *NetworkCli, nwName, svNameID string) (string, error) {
|
||||
// Sanity Check
|
||||
obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/networks?name=%s", nwName), nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var nwList []networkResource
|
||||
if err = json.Unmarshal(obj, &nwList); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(nwList) == 0 {
|
||||
return "", fmt.Errorf("Network %s does not exist", nwName)
|
||||
}
|
||||
|
||||
if nwName == "" {
|
||||
obj, _, err := readBody(cli.call("GET", "/networks/"+nwList[0].ID, nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
networkResource := &networkResource{}
|
||||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
|
||||
return "", err
|
||||
}
|
||||
nwName = networkResource.Name
|
||||
}
|
||||
|
||||
// Query service by name
|
||||
obj, statusCode, err := readBody(cli.call("GET", fmt.Sprintf("/services?name=%s", svNameID), nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("name query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj))
|
||||
}
|
||||
|
||||
var list []*serviceResource
|
||||
if err = json.Unmarshal(obj, &list); err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, sr := range list {
|
||||
if sr.Network == nwName {
|
||||
return sr.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Query service by Partial-id (this covers full id as well)
|
||||
obj, statusCode, err = readBody(cli.call("GET", fmt.Sprintf("/services?partial-id=%s", svNameID), nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("partial-id match query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj))
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(obj, &list); err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, sr := range list {
|
||||
if sr.Network == nwName {
|
||||
return sr.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Service %s not found on network %s", svNameID, nwName)
|
||||
}
|
||||
|
||||
func lookupContainerID(cli *NetworkCli, cnNameID string) (string, error) {
|
||||
// Container is a Docker resource, ask docker about it.
|
||||
// In case of connection error, we assume we are running in dnet and return whatever was passed to us
|
||||
obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/containers/%s/json", cnNameID), nil, nil))
|
||||
if err != nil {
|
||||
// We are probably running outside of docker
|
||||
return cnNameID, nil
|
||||
}
|
||||
|
||||
var x map[string]interface{}
|
||||
err = json.Unmarshal(obj, &x)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if iid, ok := x["Id"]; ok {
|
||||
if id, ok := iid.(string); ok {
|
||||
return id, nil
|
||||
}
|
||||
return "", errors.New("Unexpected data type for container ID in json response")
|
||||
}
|
||||
return "", errors.New("Cannot find container ID in json response")
|
||||
}
|
||||
|
||||
func lookupSandboxID(cli *NetworkCli, containerID string) (string, error) {
|
||||
obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/sandboxes?partial-container-id=%s", containerID), nil, nil))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var sandboxList []SandboxResource
|
||||
err = json.Unmarshal(obj, &sandboxList)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(sandboxList) == 0 {
|
||||
return "", fmt.Errorf("cannot find sandbox for container: %s", containerID)
|
||||
}
|
||||
|
||||
return sandboxList[0].ID, nil
|
||||
}
|
||||
|
||||
// CmdService handles the service UI
|
||||
func (cli *NetworkCli) CmdService(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false)
|
||||
cmd.Require(flag.Min, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err == nil {
|
||||
cmd.Usage()
|
||||
return fmt.Errorf("Invalid command : %v", args)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse service name for "SERVICE[.NETWORK]" format
|
||||
func parseServiceName(name string) (string, string) {
|
||||
s := strings.Split(name, ".")
|
||||
var sName, nName string
|
||||
if len(s) > 1 {
|
||||
nName = s[len(s)-1]
|
||||
sName = strings.Join(s[:len(s)-1], ".")
|
||||
} else {
|
||||
sName = s[0]
|
||||
}
|
||||
return sName, nName
|
||||
}
|
||||
|
||||
// CmdServicePublish handles service create UI
|
||||
func (cli *NetworkCli) CmdServicePublish(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "publish", "SERVICE[.NETWORK]", "Publish a new service on a network", false)
|
||||
flAlias := flag.NewListOpts(netutils.ValidateAlias)
|
||||
cmd.Var(&flAlias, []string{"-alias"}, "Add alias to self")
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sn, nn := parseServiceName(cmd.Arg(0))
|
||||
sc := serviceCreate{Name: sn, Network: nn, MyAliases: flAlias.GetAll()}
|
||||
obj, _, err := readBody(cli.call("POST", "/services", sc, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var replyID string
|
||||
err = json.Unmarshal(obj, &replyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "%s\n", replyID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdServiceUnpublish handles service delete UI
|
||||
func (cli *NetworkCli) CmdServiceUnpublish(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "unpublish", "SERVICE[.NETWORK]", "Removes a service", false)
|
||||
force := cmd.Bool([]string{"f", "-force"}, false, "force unpublish service")
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sn, nn := parseServiceName(cmd.Arg(0))
|
||||
serviceID, err := lookupServiceID(cli, nn, sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sd := serviceDelete{Name: sn, Force: *force}
|
||||
_, _, err = readBody(cli.call("DELETE", "/services/"+serviceID, sd, nil))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CmdServiceLs handles service list UI
|
||||
func (cli *NetworkCli) CmdServiceLs(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "ls", "SERVICE", "Lists all the services on a network", false)
|
||||
flNetwork := cmd.String([]string{"net", "-network"}, "", "Only show the services that are published on the specified network")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
|
||||
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
|
||||
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var obj []byte
|
||||
if *flNetwork == "" {
|
||||
obj, _, err = readBody(cli.call("GET", "/services", nil, nil))
|
||||
} else {
|
||||
obj, _, err = readBody(cli.call("GET", "/services?network="+*flNetwork, nil, nil))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var serviceResources []serviceResource
|
||||
err = json.Unmarshal(obj, &serviceResources)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
// unless quiet (-q) is specified, print field titles
|
||||
if !*quiet {
|
||||
fmt.Fprintln(wr, "SERVICE ID\tNAME\tNETWORK\tCONTAINER\tSANDBOX")
|
||||
}
|
||||
|
||||
for _, sr := range serviceResources {
|
||||
ID := sr.ID
|
||||
bkID, sbID, err := getBackendID(cli, ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !*noTrunc {
|
||||
ID = stringid.TruncateID(ID)
|
||||
bkID = stringid.TruncateID(bkID)
|
||||
sbID = stringid.TruncateID(sbID)
|
||||
}
|
||||
if !*quiet {
|
||||
fmt.Fprintf(wr, "%s\t%s\t%s\t%s\t%s\n", ID, sr.Name, sr.Network, bkID, sbID)
|
||||
} else {
|
||||
fmt.Fprintln(wr, ID)
|
||||
}
|
||||
}
|
||||
wr.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBackendID(cli *NetworkCli, servID string) (string, string, error) {
|
||||
var (
|
||||
obj []byte
|
||||
err error
|
||||
bk string
|
||||
sb string
|
||||
)
|
||||
|
||||
if obj, _, err = readBody(cli.call("GET", "/services/"+servID+"/backend", nil, nil)); err == nil {
|
||||
var sr SandboxResource
|
||||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&sr); err == nil {
|
||||
bk = sr.ContainerID
|
||||
sb = sr.ID
|
||||
} else {
|
||||
// Only print a message, don't make the caller cli fail for this
|
||||
fmt.Fprintf(cli.out, "Failed to retrieve backend list for service %s (%v)\n", servID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return bk, sb, err
|
||||
}
|
||||
|
||||
// CmdServiceInfo handles service info UI
|
||||
func (cli *NetworkCli) CmdServiceInfo(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "info", "SERVICE[.NETWORK]", "Displays detailed information about a service", false)
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sn, nn := parseServiceName(cmd.Arg(0))
|
||||
serviceID, err := lookupServiceID(cli, nn, sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj, _, err := readBody(cli.call("GET", "/services/"+serviceID, nil, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sr := &serviceResource{}
|
||||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(sr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "Service Id: %s\n", sr.ID)
|
||||
fmt.Fprintf(cli.out, "\tName: %s\n", sr.Name)
|
||||
fmt.Fprintf(cli.out, "\tNetwork: %s\n", sr.Network)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdServiceAttach handles service attach UI
|
||||
func (cli *NetworkCli) CmdServiceAttach(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "attach", "CONTAINER SERVICE[.NETWORK]", "Sets a container as a service backend", false)
|
||||
flAlias := flag.NewListOpts(netutils.ValidateAlias)
|
||||
cmd.Var(&flAlias, []string{"-alias"}, "Add alias for another container")
|
||||
cmd.Require(flag.Min, 2)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containerID, err := lookupContainerID(cli, cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sandboxID, err := lookupSandboxID(cli, containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sn, nn := parseServiceName(cmd.Arg(1))
|
||||
serviceID, err := lookupServiceID(cli, nn, sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nc := serviceAttach{SandboxID: sandboxID, Aliases: flAlias.GetAll()}
|
||||
|
||||
_, _, err = readBody(cli.call("POST", "/services/"+serviceID+"/backend", nc, nil))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CmdServiceDetach handles service detach UI
|
||||
func (cli *NetworkCli) CmdServiceDetach(chain string, args ...string) error {
|
||||
cmd := cli.Subcmd(chain, "detach", "CONTAINER SERVICE", "Removes a container from service backend", false)
|
||||
cmd.Require(flag.Min, 2)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sn, nn := parseServiceName(cmd.Arg(1))
|
||||
containerID, err := lookupContainerID(cli, cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sandboxID, err := lookupSandboxID(cli, containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceID, err := lookupServiceID(cli, nn, sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = readBody(cli.call("DELETE", "/services/"+serviceID+"/backend/"+sandboxID, nil, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func serviceUsage(chain string) string {
|
||||
help := "Commands:\n"
|
||||
|
||||
for _, cmd := range serviceCommands {
|
||||
help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description)
|
||||
}
|
||||
|
||||
help += fmt.Sprintf("\nRun '%s service COMMAND --help' for more information on a command.", chain)
|
||||
return help
|
||||
}
|
88
libnetwork/client/types.go
Normal file
88
libnetwork/client/types.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package client
|
||||
|
||||
import "github.com/docker/docker/libnetwork/types"
|
||||
|
||||
/***********
|
||||
Resources
|
||||
************/
|
||||
|
||||
// networkResource is the body of the "get network" http response message
|
||||
type networkResource struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Services []*serviceResource `json:"services"`
|
||||
}
|
||||
|
||||
// serviceResource is the body of the "get service" http response message
|
||||
type serviceResource struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
// SandboxResource is the body of "get service backend" response message
|
||||
type SandboxResource struct {
|
||||
ID string `json:"id"`
|
||||
Key string `json:"key"`
|
||||
ContainerID string `json:"container_id"`
|
||||
}
|
||||
|
||||
/***********
|
||||
Body types
|
||||
************/
|
||||
type ipamConf struct {
|
||||
PreferredPool string
|
||||
SubPool string
|
||||
Gateway string
|
||||
AuxAddresses map[string]string
|
||||
}
|
||||
|
||||
// networkCreate is the expected body of the "create network" http request message
|
||||
type networkCreate struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
NetworkType string `json:"network_type"`
|
||||
IPv4Conf []ipamConf `json:"ipv4_configuration"`
|
||||
DriverOpts map[string]string `json:"driver_opts"`
|
||||
NetworkOpts map[string]string `json:"network_opts"`
|
||||
}
|
||||
|
||||
// serviceCreate represents the body of the "publish service" http request message
|
||||
type serviceCreate struct {
|
||||
Name string `json:"name"`
|
||||
MyAliases []string `json:"my_aliases"`
|
||||
Network string `json:"network_name"`
|
||||
}
|
||||
|
||||
// serviceDelete represents the body of the "unpublish service" http request message
|
||||
type serviceDelete struct {
|
||||
Name string `json:"name"`
|
||||
Force bool `json:"force"`
|
||||
}
|
||||
|
||||
// serviceAttach represents the expected body of the "attach/detach sandbox to/from service" http request messages
|
||||
type serviceAttach struct {
|
||||
SandboxID string `json:"sandbox_id"`
|
||||
Aliases []string `json:"aliases"`
|
||||
}
|
||||
|
||||
// SandboxCreate is the body of the "post /sandboxes" http request message
|
||||
type SandboxCreate struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
HostName string `json:"host_name"`
|
||||
DomainName string `json:"domain_name"`
|
||||
HostsPath string `json:"hosts_path"`
|
||||
ResolvConfPath string `json:"resolv_conf_path"`
|
||||
DNS []string `json:"dns"`
|
||||
ExtraHosts []extraHost `json:"extra_hosts"`
|
||||
UseDefaultSandbox bool `json:"use_default_sandbox"`
|
||||
ExposedPorts []types.TransportPort `json:"exposed_ports"`
|
||||
PortMapping []types.PortBinding `json:"port_mapping"`
|
||||
}
|
||||
|
||||
// extraHost represents the extra host object
|
||||
type extraHost struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
}
|
4
libnetwork/cmd/diagnostic/Dockerfile.client
Normal file
4
libnetwork/cmd/diagnostic/Dockerfile.client
Normal file
|
@ -0,0 +1,4 @@
|
|||
FROM alpine
|
||||
RUN apk add --no-cache curl
|
||||
COPY diagnosticClient /usr/local/bin/diagnosticClient
|
||||
ENTRYPOINT ["/usr/local/bin/diagnosticClient"]
|
5
libnetwork/cmd/diagnostic/Dockerfile.dind
Normal file
5
libnetwork/cmd/diagnostic/Dockerfile.dind
Normal file
|
@ -0,0 +1,5 @@
|
|||
FROM docker:17.12-dind
|
||||
RUN apk add --no-cache curl
|
||||
ENV DIND_CLIENT=true
|
||||
COPY daemon.json /etc/docker/daemon.json
|
||||
COPY diagnosticClient /usr/local/bin/diagnosticClient
|
262
libnetwork/cmd/diagnostic/README.md
Normal file
262
libnetwork/cmd/diagnostic/README.md
Normal file
|
@ -0,0 +1,262 @@
|
|||
---
|
||||
description: Learn to use the built-in network debugger to debug overlay networking problems
|
||||
keywords: network, troubleshooting, debug
|
||||
title: Debug overlay or swarm networking issues
|
||||
---
|
||||
|
||||
**WARNING**
|
||||
This tool can change the internal state of the libnetwork API, be really mindful
|
||||
on its use and read carefully the following guide. Improper use of it will damage
|
||||
or permanently destroy the network configuration.
|
||||
|
||||
|
||||
Docker CE 17.12 and higher introduce a network debugging tool designed to help
|
||||
debug issues with overlay networks and swarm services running on Linux hosts.
|
||||
When enabled, a network diagnostic server listens on the specified port and
|
||||
provides diagnostic information. The network debugging tool should only be
|
||||
started to debug specific issues, and should not be left running all the time.
|
||||
|
||||
Information about networks is stored in the database, which can be examined using
|
||||
the API. Currently the database contains information about the overlay network
|
||||
as well as the service discovery data.
|
||||
|
||||
The Docker API exposes endpoints to query and control the network debugging
|
||||
tool. CLI integration is provided as a preview, but the implementation is not
|
||||
yet considered stable and commands and options may change without notice.
|
||||
|
||||
The tool is available into 2 forms:
|
||||
1) client only: dockereng/network-diagnostic:onlyclient
|
||||
2) docker in docker version: dockereng/network-diagnostic:17.12-dind
|
||||
The latter allows to use the tool with a cluster running an engine older than 17.12
|
||||
|
||||
## Enable the diagnostic server
|
||||
|
||||
The tool currently only works on Docker hosts running on Linux. To enable it on a node
|
||||
follow the step below.
|
||||
|
||||
1. Set the `network-diagnostic-port` to a port which is free on the Docker
|
||||
host, in the `/etc/docker/daemon.json` configuration file.
|
||||
|
||||
```json
|
||||
“network-diagnostic-port”: <port>
|
||||
```
|
||||
|
||||
2. Get the process ID (PID) of the `dockerd` process. It is the second field in
|
||||
the output, and is typically a number from 2 to 6 digits long.
|
||||
|
||||
```bash
|
||||
$ ps aux |grep dockerd | grep -v grep
|
||||
```
|
||||
|
||||
3. Reload the Docker configuration without restarting Docker, by sending the
|
||||
`HUP` signal to the PID you found in the previous step.
|
||||
|
||||
```bash
|
||||
kill -HUP <pid-of-dockerd>
|
||||
```
|
||||
|
||||
If systemd is used the command `systemctl reload docker` will be enough
|
||||
|
||||
|
||||
A message like the following will appear in the Docker host logs:
|
||||
|
||||
```none
|
||||
Starting the diagnostic server listening on <port> for commands
|
||||
```
|
||||
|
||||
## Disable the diagnostic tool
|
||||
|
||||
Repeat these steps for each node participating in the swarm.
|
||||
|
||||
1. Remove the `network-diagnostic-port` key from the `/etc/docker/daemon.json`
|
||||
configuration file.
|
||||
|
||||
2. Get the process ID (PID) of the `dockerd` process. It is the second field in
|
||||
the output, and is typically a number from 2 to 6 digits long.
|
||||
|
||||
```bash
|
||||
$ ps aux |grep dockerd | grep -v grep
|
||||
```
|
||||
|
||||
3. Reload the Docker configuration without restarting Docker, by sending the
|
||||
`HUP` signal to the PID you found in the previous step.
|
||||
|
||||
```bash
|
||||
kill -HUP <pid-of-dockerd>
|
||||
```
|
||||
|
||||
A message like the following will appear in the Docker host logs:
|
||||
|
||||
```none
|
||||
Disabling the diagnostic server
|
||||
```
|
||||
|
||||
## Access the diagnostic tool's API
|
||||
|
||||
The network diagnostic tool exposes its own RESTful API. To access the API,
|
||||
send a HTTP request to the port where the tool is listening. The following
|
||||
commands assume the tool is listening on port 2000.
|
||||
|
||||
Examples are not given for every endpoint.
|
||||
|
||||
### Get help
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/help
|
||||
|
||||
OK
|
||||
/updateentry
|
||||
/getentry
|
||||
/gettable
|
||||
/leavenetwork
|
||||
/createentry
|
||||
/help
|
||||
/clusterpeers
|
||||
/ready
|
||||
/joinnetwork
|
||||
/deleteentry
|
||||
/networkpeers
|
||||
/
|
||||
/join
|
||||
```
|
||||
|
||||
### Join or leave the network database cluster
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/join?members=ip1,ip2,...
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/leave?members=ip1,ip2,...
|
||||
```
|
||||
|
||||
`ip1`, `ip2`, ... are the swarm node ips (usually one is enough)
|
||||
|
||||
### Join or leave a network
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/joinnetwork?nid=<network id>
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/leavenetwork?nid=<network id>
|
||||
```
|
||||
|
||||
`network id` can be retrieved on the manager with `docker network ls --no-trunc` and has
|
||||
to be the full length identifier
|
||||
|
||||
### List cluster peers
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/clusterpeers
|
||||
```
|
||||
|
||||
### List nodes connected to a given network
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/networkpeers?nid=<network id>
|
||||
```
|
||||
`network id` can be retrieved on the manager with `docker network ls --no-trunc` and has
|
||||
to be the full length identifier
|
||||
|
||||
### Dump database tables
|
||||
|
||||
The tables are called `endpoint_table` and `overlay_peer_table`.
|
||||
The `overlay_peer_table` contains all the overlay forwarding information
|
||||
The `endpoint_table` contains all the service discovery information
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/gettable?nid=<network id>&tname=<table name>
|
||||
```
|
||||
|
||||
### Interact with a specific database table
|
||||
|
||||
The tables are called `endpoint_table` and `overlay_peer_table`.
|
||||
|
||||
```bash
|
||||
$ curl localhost:2000/<method>?nid=<network id>&tname=<table name>&key=<key>[&value=<value>]
|
||||
```
|
||||
|
||||
Note:
|
||||
operations on tables have node ownership, this means that are going to remain persistent till
|
||||
the node that inserted them is part of the cluster
|
||||
|
||||
## Access the diagnostic tool's CLI
|
||||
|
||||
The CLI is provided as a preview and is not yet stable. Commands or options may
|
||||
change at any time.
|
||||
|
||||
The CLI executable is called `diagnosticClient` and is made available using a
|
||||
standalone container.
|
||||
|
||||
`docker run --net host dockereng/network-diagnostic:onlyclient -v -net <full network id> -t sd`
|
||||
|
||||
The following flags are supported:
|
||||
|
||||
| Flag | Description |
|
||||
|---------------|-------------------------------------------------|
|
||||
| -t <string> | Table one of `sd` or `overlay`. |
|
||||
| -ip <string> | The IP address to query. Defaults to 127.0.0.1. |
|
||||
| -net <string> | The target network ID. |
|
||||
| -port <int> | The target port. (default port is 2000) |
|
||||
| -a | Join/leave network |
|
||||
| -v | Enable verbose output. |
|
||||
|
||||
*NOTE*
|
||||
By default the tool won't try to join the network. This is following the intent to not change
|
||||
the state on which the node is when the diagnostic client is run. This means that it is safe
|
||||
to run the diagnosticClient against a running daemon because it will just dump the current state.
|
||||
When using instead the diagnosticClient in the containerized version the flag `-a` MUST be passed
|
||||
to avoid retrieving empty results. On the other side using the `-a` flag against a loaded daemon
|
||||
will have the undesirable side effect to leave the network and so cutting down the data path for
|
||||
that daemon.
|
||||
|
||||
### Container version of the diagnostic tool
|
||||
|
||||
The CLI is provided as a container with a 17.12 engine that needs to run using privileged mode.
|
||||
*NOTE*
|
||||
Remember that table operations have ownership, so any `create entry` will be persistent till
|
||||
the diagnostic container is part of the swarm.
|
||||
|
||||
1. Make sure that the node where the diagnostic client will run is not part of the swarm, if so do `docker swarm leave -f`
|
||||
|
||||
2. To run the container, use a command like the following:
|
||||
|
||||
```bash
|
||||
$ docker container run --name net-diagnostic -d --privileged --network host dockereng/network-diagnostic:17.12-dind
|
||||
```
|
||||
|
||||
3. Connect to the container using `docker exec -it <container-ID> sh`,
|
||||
and start the server using the following command:
|
||||
|
||||
```bash
|
||||
$ kill -HUP 1
|
||||
```
|
||||
|
||||
4. Join the diagnostic container to the swarm, then run the diagnostic CLI within the container.
|
||||
|
||||
```bash
|
||||
$ ./diagnosticClient <flags>...
|
||||
```
|
||||
|
||||
4. When finished debugging, leave the swarm and stop the container.
|
||||
|
||||
### Examples
|
||||
|
||||
The following commands dump the service discovery table and verify node
|
||||
ownership.
|
||||
|
||||
*NOTE*
|
||||
Remember to use the full network ID, you can easily find that with `docker network ls --no-trunc`
|
||||
|
||||
**Service discovery and load balancer:**
|
||||
|
||||
```bash
|
||||
$ diagnostiClient -t sd -v -net n8a8ie6tb3wr2e260vxj8ncy4 -a
|
||||
```
|
||||
|
||||
**Overlay network:**
|
||||
|
||||
```bash
|
||||
$ diagnostiClient -port 2001 -t overlay -v -net n8a8ie6tb3wr2e260vxj8ncy4 -a
|
||||
```
|
4
libnetwork/cmd/diagnostic/daemon.json
Normal file
4
libnetwork/cmd/diagnostic/daemon.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"debug": true,
|
||||
"network-diagnostic-port": 2000
|
||||
}
|
209
libnetwork/cmd/diagnostic/main.go
Normal file
209
libnetwork/cmd/diagnostic/main.go
Normal file
|
@ -0,0 +1,209 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork/diagnostic"
|
||||
"github.com/docker/docker/libnetwork/drivers/overlay"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
readyPath = "http://%s:%d/ready"
|
||||
joinNetwork = "http://%s:%d/joinnetwork?nid=%s"
|
||||
leaveNetwork = "http://%s:%d/leavenetwork?nid=%s"
|
||||
clusterPeers = "http://%s:%d/clusterpeers?json"
|
||||
networkPeers = "http://%s:%d/networkpeers?nid=%s&json"
|
||||
dumpTable = "http://%s:%d/gettable?nid=%s&tname=%s&json"
|
||||
deleteEntry = "http://%s:%d/deleteentry?nid=%s&tname=%s&key=%s&json"
|
||||
)
|
||||
|
||||
func httpIsOk(body io.ReadCloser) {
|
||||
b, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed the body parse %s", err)
|
||||
}
|
||||
if !strings.Contains(string(b), "OK") {
|
||||
logrus.Fatalf("Server not ready %s", b)
|
||||
}
|
||||
body.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
ipPtr := flag.String("ip", "127.0.0.1", "ip address")
|
||||
portPtr := flag.Int("port", 2000, "port")
|
||||
networkPtr := flag.String("net", "", "target network")
|
||||
tablePtr := flag.String("t", "", "table to process <sd/overlay>")
|
||||
remediatePtr := flag.Bool("r", false, "perform remediation deleting orphan entries")
|
||||
joinPtr := flag.Bool("a", false, "join/leave network")
|
||||
verbosePtr := flag.Bool("v", false, "verbose output")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *verbosePtr {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
if _, ok := os.LookupEnv("DIND_CLIENT"); !ok && *joinPtr {
|
||||
logrus.Fatal("you are not using the client in docker in docker mode, the use of the -a flag can be disruptive, " +
|
||||
"please remove it (doc:https://github.com/docker/docker/libnetwork/blob/master/cmd/diagnostic/README.md)")
|
||||
}
|
||||
|
||||
logrus.Infof("Connecting to %s:%d checking ready", *ipPtr, *portPtr)
|
||||
resp, err := http.Get(fmt.Sprintf(readyPath, *ipPtr, *portPtr))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("The connection failed")
|
||||
}
|
||||
httpIsOk(resp.Body)
|
||||
|
||||
clusterPeers := fetchNodePeers(*ipPtr, *portPtr, "")
|
||||
var networkPeers map[string]string
|
||||
var joinedNetwork bool
|
||||
if *networkPtr != "" {
|
||||
if *joinPtr {
|
||||
logrus.Infof("Joining the network:%q", *networkPtr)
|
||||
resp, err = http.Get(fmt.Sprintf(joinNetwork, *ipPtr, *portPtr, *networkPtr))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed joining the network")
|
||||
}
|
||||
httpIsOk(resp.Body)
|
||||
joinedNetwork = true
|
||||
}
|
||||
|
||||
networkPeers = fetchNodePeers(*ipPtr, *portPtr, *networkPtr)
|
||||
if len(networkPeers) == 0 {
|
||||
logrus.Warnf("There is no peer on network %q, check the network ID, and verify that is the non truncated version", *networkPtr)
|
||||
}
|
||||
}
|
||||
|
||||
switch *tablePtr {
|
||||
case "sd":
|
||||
fetchTable(*ipPtr, *portPtr, *networkPtr, "endpoint_table", clusterPeers, networkPeers, *remediatePtr)
|
||||
case "overlay":
|
||||
fetchTable(*ipPtr, *portPtr, *networkPtr, "overlay_peer_table", clusterPeers, networkPeers, *remediatePtr)
|
||||
}
|
||||
|
||||
if joinedNetwork {
|
||||
logrus.Infof("Leaving the network:%q", *networkPtr)
|
||||
resp, err = http.Get(fmt.Sprintf(leaveNetwork, *ipPtr, *portPtr, *networkPtr))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed leaving the network")
|
||||
}
|
||||
httpIsOk(resp.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchNodePeers(ip string, port int, network string) map[string]string {
|
||||
if network == "" {
|
||||
logrus.Infof("Fetch cluster peers")
|
||||
} else {
|
||||
logrus.Infof("Fetch peers network:%q", network)
|
||||
}
|
||||
|
||||
var path string
|
||||
if network != "" {
|
||||
path = fmt.Sprintf(networkPeers, ip, port, network)
|
||||
} else {
|
||||
path = fmt.Sprintf(clusterPeers, ip, port)
|
||||
}
|
||||
|
||||
resp, err := http.Get(path) // nolint:gosec
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed fetching path")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed the body parse")
|
||||
}
|
||||
|
||||
output := diagnostic.HTTPResult{Details: &diagnostic.TablePeersResult{}}
|
||||
err = json.Unmarshal(body, &output)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed the json unmarshalling")
|
||||
}
|
||||
|
||||
logrus.Debugf("Parsing JSON response")
|
||||
result := make(map[string]string, output.Details.(*diagnostic.TablePeersResult).Length)
|
||||
for _, v := range output.Details.(*diagnostic.TablePeersResult).Elements {
|
||||
logrus.Debugf("name:%s ip:%s", v.Name, v.IP)
|
||||
result[v.Name] = v.IP
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func fetchTable(ip string, port int, network, tableName string, clusterPeers, networkPeers map[string]string, remediate bool) {
|
||||
logrus.Infof("Fetch %s table and check owners", tableName)
|
||||
resp, err := http.Get(fmt.Sprintf(dumpTable, ip, port, network, tableName))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed fetching endpoint table")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed the body parse")
|
||||
}
|
||||
|
||||
output := diagnostic.HTTPResult{Details: &diagnostic.TableEndpointsResult{}}
|
||||
err = json.Unmarshal(body, &output)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed the json unmarshalling")
|
||||
}
|
||||
|
||||
logrus.Debug("Parsing data structures")
|
||||
var orphanKeys []string
|
||||
for _, v := range output.Details.(*diagnostic.TableEndpointsResult).Elements {
|
||||
decoded, err := base64.StdEncoding.DecodeString(v.Value)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed decoding entry")
|
||||
continue
|
||||
}
|
||||
switch tableName {
|
||||
case "endpoint_table":
|
||||
var elem libnetwork.EndpointRecord
|
||||
elem.Unmarshal(decoded)
|
||||
logrus.Debugf("key:%s value:%+v owner:%s", v.Key, elem, v.Owner)
|
||||
case "overlay_peer_table":
|
||||
var elem overlay.PeerRecord
|
||||
elem.Unmarshal(decoded)
|
||||
logrus.Debugf("key:%s value:%+v owner:%s", v.Key, elem, v.Owner)
|
||||
}
|
||||
|
||||
if _, ok := networkPeers[v.Owner]; !ok {
|
||||
logrus.Warnf("The element with key:%s does not belong to any node on this network", v.Key)
|
||||
orphanKeys = append(orphanKeys, v.Key)
|
||||
}
|
||||
if _, ok := clusterPeers[v.Owner]; !ok {
|
||||
logrus.Warnf("The element with key:%s does not belong to any node on this cluster", v.Key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(orphanKeys) > 0 && remediate {
|
||||
logrus.Warnf("The following keys:%v results as orphan, do you want to proceed with the deletion (this operation is irreversible)? [Yes/No]", orphanKeys)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
text = strings.Replace(text, "\n", "", -1)
|
||||
if strings.Compare(text, "Yes") == 0 {
|
||||
for _, k := range orphanKeys {
|
||||
resp, err := http.Get(fmt.Sprintf(deleteEntry, ip, port, network, tableName, k))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed deleting entry k:%s", k)
|
||||
break
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
} else {
|
||||
logrus.Infof("Deletion skipped")
|
||||
}
|
||||
}
|
||||
}
|
146
libnetwork/cmd/dnet/cmd.go
Normal file
146
libnetwork/cmd/dnet/cmd.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/libnetwork/client"
|
||||
"github.com/moby/term"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
containerCreateCommand = cli.Command{
|
||||
Name: "create",
|
||||
Usage: "Create a container",
|
||||
Action: runContainerCreate,
|
||||
}
|
||||
|
||||
containerRmCommand = cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "Remove a container",
|
||||
Action: runContainerRm,
|
||||
}
|
||||
|
||||
containerCommands = []cli.Command{
|
||||
containerCreateCommand,
|
||||
containerRmCommand,
|
||||
}
|
||||
|
||||
dnetCommands = []cli.Command{
|
||||
createDockerCommand("network"),
|
||||
createDockerCommand("service"),
|
||||
{
|
||||
Name: "container",
|
||||
Usage: "Container management commands",
|
||||
Subcommands: containerCommands,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runContainerCreate(c *cli.Context) {
|
||||
if len(c.Args()) == 0 {
|
||||
fmt.Println("Please provide container id argument")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sc := client.SandboxCreate{ContainerID: c.Args()[0]}
|
||||
obj, _, err := readBody(epConn.httpCall("POST", "/sandboxes", sc, nil))
|
||||
if err != nil {
|
||||
fmt.Printf("POST failed during create container: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var replyID string
|
||||
err = json.Unmarshal(obj, &replyID)
|
||||
if err != nil {
|
||||
fmt.Printf("Unmarshall of response failed during create container: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", replyID)
|
||||
|
||||
}
|
||||
|
||||
func runContainerRm(c *cli.Context) {
|
||||
var sbList []*client.SandboxResource
|
||||
|
||||
if len(c.Args()) == 0 {
|
||||
fmt.Println("Please provide container id argument")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
obj, _, err := readBody(epConn.httpCall("GET", "/sandboxes?partial-container-id="+c.Args()[0], nil, nil))
|
||||
if err != nil {
|
||||
fmt.Printf("GET failed during container id lookup: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(obj, &sbList)
|
||||
if err != nil {
|
||||
fmt.Printf("Unmarshall of container id lookup response failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(sbList) == 0 {
|
||||
fmt.Printf("No sandbox for container %s found\n", c.Args()[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, _, err = readBody(epConn.httpCall("DELETE", "/sandboxes/"+sbList[0].ID, nil, nil))
|
||||
if err != nil {
|
||||
fmt.Printf("DELETE of sandbox id %s failed: %v", sbList[0].ID, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func runDockerCommand(c *cli.Context, cmd string) {
|
||||
_, stdout, stderr := term.StdStreams()
|
||||
oldcli := client.NewNetworkCli(stdout, stderr, epConn.httpCall)
|
||||
var args []string
|
||||
args = append(args, cmd)
|
||||
if c.Bool("h") {
|
||||
args = append(args, "--help")
|
||||
} else {
|
||||
args = append(args, c.Args()...)
|
||||
}
|
||||
if err := oldcli.Cmd("dnet", args...); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func createDockerCommand(cmd string) cli.Command {
|
||||
return cli.Command{
|
||||
Name: cmd,
|
||||
Usage: fmt.Sprintf("%s management commands", cmd),
|
||||
SkipFlagParsing: true,
|
||||
Action: func(c *cli.Context) {
|
||||
runDockerCommand(c, cmd)
|
||||
},
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "h, -help",
|
||||
Usage: fmt.Sprintf("%s help", cmd),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func readBody(stream io.ReadCloser, hdr http.Header, statusCode int, err error) ([]byte, int, error) {
|
||||
if stream != nil {
|
||||
defer stream.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, statusCode, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(stream)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
return body, statusCode, nil
|
||||
}
|
576
libnetwork/cmd/dnet/dnet.go
Normal file
576
libnetwork/cmd/dnet/dnet.go
Normal file
|
@ -0,0 +1,576 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/libnetwork/api"
|
||||
"github.com/docker/docker/libnetwork/cluster"
|
||||
"github.com/docker/docker/libnetwork/config"
|
||||
"github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
"github.com/docker/docker/libnetwork/netlabel"
|
||||
"github.com/docker/docker/libnetwork/netutils"
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/discovery"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/moby/term"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultHTTPHost is used if only port is provided to -H flag e.g. docker -d -H tcp://:8080
|
||||
DefaultHTTPHost = "0.0.0.0"
|
||||
// DefaultHTTPPort is the default http port used by dnet
|
||||
DefaultHTTPPort = 2385
|
||||
cfgFileEnv = "LIBNETWORK_CFG"
|
||||
defaultCfgFile = "/etc/default/libnetwork.toml"
|
||||
defaultHeartbeat = time.Duration(10) * time.Second
|
||||
ttlFactor = 2
|
||||
)
|
||||
|
||||
var epConn *dnetConnection
|
||||
|
||||
func main() {
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
|
||||
_, stdout, stderr := term.StdStreams()
|
||||
logrus.SetOutput(stderr)
|
||||
|
||||
err := dnetApp(stdout, stderr)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig parses the libnetwork configuration file
|
||||
func (d *dnetConnection) parseOrchestrationConfig(tomlCfgFile string) error {
|
||||
dummy := &dnetConnection{}
|
||||
|
||||
if _, err := toml.DecodeFile(tomlCfgFile, dummy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dummy.Orchestration != nil {
|
||||
d.Orchestration = dummy.Orchestration
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dnetConnection) parseConfig(cfgFile string) (*config.Config, error) {
|
||||
if strings.Trim(cfgFile, " ") == "" {
|
||||
cfgFile = os.Getenv(cfgFileEnv)
|
||||
if strings.Trim(cfgFile, " ") == "" {
|
||||
cfgFile = defaultCfgFile
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.parseOrchestrationConfig(cfgFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config.ParseConfig(cfgFile)
|
||||
}
|
||||
|
||||
func processConfig(cfg *config.Config) []config.Option {
|
||||
options := []config.Option{}
|
||||
if cfg == nil {
|
||||
return options
|
||||
}
|
||||
|
||||
dn := "bridge"
|
||||
if strings.TrimSpace(cfg.Daemon.DefaultNetwork) != "" {
|
||||
dn = cfg.Daemon.DefaultNetwork
|
||||
}
|
||||
options = append(options, config.OptionDefaultNetwork(dn))
|
||||
|
||||
dd := "bridge"
|
||||
if strings.TrimSpace(cfg.Daemon.DefaultDriver) != "" {
|
||||
dd = cfg.Daemon.DefaultDriver
|
||||
}
|
||||
options = append(options, config.OptionDefaultDriver(dd))
|
||||
|
||||
if cfg.Daemon.Labels != nil {
|
||||
options = append(options, config.OptionLabels(cfg.Daemon.Labels))
|
||||
}
|
||||
|
||||
if dcfg, ok := cfg.Scopes[datastore.GlobalScope]; ok && dcfg.IsValid() {
|
||||
options = append(options, config.OptionKVProvider(dcfg.Client.Provider))
|
||||
options = append(options, config.OptionKVProviderURL(dcfg.Client.Address))
|
||||
}
|
||||
|
||||
dOptions, err := startDiscovery(&cfg.Cluster)
|
||||
if err != nil {
|
||||
logrus.Infof("Skipping discovery : %s", err.Error())
|
||||
} else {
|
||||
options = append(options, dOptions...)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func startDiscovery(cfg *config.ClusterCfg) ([]config.Option, error) {
|
||||
if cfg == nil {
|
||||
return nil, errors.New("discovery requires a valid configuration")
|
||||
}
|
||||
|
||||
hb := time.Duration(cfg.Heartbeat) * time.Second
|
||||
if hb == 0 {
|
||||
hb = defaultHeartbeat
|
||||
}
|
||||
logrus.Infof("discovery : %s %s", cfg.Discovery, hb.String())
|
||||
d, err := discovery.New(cfg.Discovery, hb, ttlFactor*hb, map[string]string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.Address == "" {
|
||||
iface, err := net.InterfaceByName("eth0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil || len(addrs) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
ip, _, _ := net.ParseCIDR(addrs[0].String())
|
||||
cfg.Address = ip.String()
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(cfg.Address); ip == nil {
|
||||
return nil, errors.New("address config should be either ipv4 or ipv6 address")
|
||||
}
|
||||
|
||||
if err := d.Register(cfg.Address + ":0"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options := []config.Option{config.OptionDiscoveryWatcher(d), config.OptionDiscoveryAddress(cfg.Address)}
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(hb)
|
||||
if err := d.Register(cfg.Address + ":0"); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func dnetApp(stdout, stderr io.Writer) error {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = "dnet"
|
||||
app.Usage = "A self-sufficient runtime for container networking."
|
||||
app.Flags = dnetFlags
|
||||
app.Before = processFlags
|
||||
app.Commands = dnetCommands
|
||||
|
||||
app.Run(os.Args)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDefaultNetwork(c libnetwork.NetworkController) {
|
||||
nw := c.Config().Daemon.DefaultNetwork
|
||||
d := c.Config().Daemon.DefaultDriver
|
||||
createOptions := []libnetwork.NetworkOption{}
|
||||
genericOption := options.Generic{}
|
||||
|
||||
if nw != "" && d != "" {
|
||||
// Bridge driver is special due to legacy reasons
|
||||
if d == "bridge" {
|
||||
genericOption[netlabel.GenericData] = map[string]string{
|
||||
"BridgeName": "docker0",
|
||||
"DefaultBridge": "true",
|
||||
}
|
||||
createOptions = append(createOptions,
|
||||
libnetwork.NetworkOptionGeneric(genericOption),
|
||||
ipamOption(nw))
|
||||
}
|
||||
|
||||
if n, err := c.NetworkByName(nw); err == nil {
|
||||
logrus.Debugf("Default network %s already present. Deleting it", nw)
|
||||
if err = n.Delete(); err != nil {
|
||||
logrus.Debugf("Network could not be deleted: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err := c.NewNetwork(d, nw, "", createOptions...)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error creating default network : %s : %v", nw, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type dnetConnection struct {
|
||||
// proto holds the client protocol i.e. unix.
|
||||
proto string
|
||||
// addr holds the client address.
|
||||
addr string
|
||||
Orchestration *NetworkOrchestration
|
||||
configEvent chan cluster.ConfigEventType
|
||||
}
|
||||
|
||||
// NetworkOrchestration exported
|
||||
type NetworkOrchestration struct {
|
||||
Agent bool
|
||||
Manager bool
|
||||
Bind string
|
||||
Peer string
|
||||
}
|
||||
|
||||
func (d *dnetConnection) dnetDaemon(cfgFile string) error {
|
||||
if err := startTestDriver(); err != nil {
|
||||
return fmt.Errorf("failed to start test driver: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := d.parseConfig(cfgFile)
|
||||
var cOptions []config.Option
|
||||
if err == nil {
|
||||
cOptions = processConfig(cfg)
|
||||
} else {
|
||||
logrus.Errorf("Error parsing config %v", err)
|
||||
}
|
||||
|
||||
bridgeConfig := options.Generic{
|
||||
"EnableIPForwarding": true,
|
||||
"EnableIPTables": true,
|
||||
}
|
||||
|
||||
bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig}
|
||||
|
||||
cOptions = append(cOptions, config.OptionDriverConfig("bridge", bridgeOption))
|
||||
|
||||
controller, err := libnetwork.New(cOptions...)
|
||||
if err != nil {
|
||||
fmt.Println("Error starting dnetDaemon :", err)
|
||||
return err
|
||||
}
|
||||
controller.SetClusterProvider(d)
|
||||
|
||||
if d.Orchestration.Agent || d.Orchestration.Manager {
|
||||
d.configEvent <- cluster.EventNodeReady
|
||||
}
|
||||
|
||||
createDefaultNetwork(controller)
|
||||
httpHandler := api.NewHTTPHandler(controller)
|
||||
r := mux.NewRouter().StrictSlash(false)
|
||||
post := r.PathPrefix("/{.*}/networks").Subrouter()
|
||||
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
|
||||
post = r.PathPrefix("/networks").Subrouter()
|
||||
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
|
||||
post = r.PathPrefix("/{.*}/services").Subrouter()
|
||||
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
|
||||
post = r.PathPrefix("/services").Subrouter()
|
||||
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
|
||||
post = r.PathPrefix("/{.*}/sandboxes").Subrouter()
|
||||
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
|
||||
post = r.PathPrefix("/sandboxes").Subrouter()
|
||||
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
|
||||
|
||||
handleSignals(controller)
|
||||
setupDumpStackTrap()
|
||||
|
||||
return http.ListenAndServe(d.addr, r)
|
||||
}
|
||||
|
||||
func (d *dnetConnection) IsManager() bool {
|
||||
return d.Orchestration.Manager
|
||||
}
|
||||
|
||||
func (d *dnetConnection) IsAgent() bool {
|
||||
return d.Orchestration.Agent
|
||||
}
|
||||
|
||||
func (d *dnetConnection) GetAdvertiseAddress() string {
|
||||
return d.Orchestration.Bind
|
||||
}
|
||||
|
||||
func (d *dnetConnection) GetDataPathAddress() string {
|
||||
return d.Orchestration.Bind
|
||||
}
|
||||
|
||||
func (d *dnetConnection) GetLocalAddress() string {
|
||||
return d.Orchestration.Bind
|
||||
}
|
||||
|
||||
func (d *dnetConnection) GetListenAddress() string {
|
||||
return d.Orchestration.Bind
|
||||
}
|
||||
|
||||
func (d *dnetConnection) GetRemoteAddressList() []string {
|
||||
return []string{d.Orchestration.Peer}
|
||||
}
|
||||
|
||||
func (d *dnetConnection) ListenClusterEvents() <-chan cluster.ConfigEventType {
|
||||
return d.configEvent
|
||||
}
|
||||
|
||||
func (d *dnetConnection) AttachNetwork(string, string, []string) (*network.NetworkingConfig, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *dnetConnection) DetachNetwork(string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dnetConnection) UpdateAttachment(string, string, *network.NetworkingConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dnetConnection) WaitForDetachment(context.Context, string, string, string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleSignals(controller libnetwork.NetworkController) {
|
||||
c := make(chan os.Signal, 1)
|
||||
signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}
|
||||
signal.Notify(c, signals...)
|
||||
go func() {
|
||||
for range c {
|
||||
controller.Stop()
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func startTestDriver() error {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
if server == nil {
|
||||
return errors.New("Failed to start an HTTP Server")
|
||||
}
|
||||
|
||||
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, `{"Scope":"global"}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, "null")
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, "null")
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, "null")
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, "null")
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, "null")
|
||||
})
|
||||
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprint(w, "null")
|
||||
})
|
||||
|
||||
if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile("/etc/docker/plugins/test.spec", []byte(server.URL), 0644)
|
||||
}
|
||||
|
||||
func newDnetConnection(val string) (*dnetConnection, error) {
|
||||
url, err := parseHost(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protoAddrParts := strings.SplitN(url, "://", 2)
|
||||
if len(protoAddrParts) != 2 {
|
||||
return nil, errors.New("bad format, expected tcp://ADDR")
|
||||
}
|
||||
if strings.ToLower(protoAddrParts[0]) != "tcp" {
|
||||
return nil, errors.New("dnet currently only supports tcp transport")
|
||||
}
|
||||
|
||||
return &dnetConnection{protoAddrParts[0], protoAddrParts[1], &NetworkOrchestration{}, make(chan cluster.ConfigEventType, 10)}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
defaultUnixSocket = "unix:///var/run/docker.sock"
|
||||
defaultTCPHost = "tcp://localhost:2375"
|
||||
)
|
||||
|
||||
// parseHost and set defaults for a Daemon host string.
|
||||
func parseHost(val string) (string, error) {
|
||||
host := strings.TrimSpace(val)
|
||||
if host == "" {
|
||||
return defaultUnixSocket, nil
|
||||
}
|
||||
|
||||
addrParts := strings.SplitN(host, "://", 2)
|
||||
if len(addrParts) == 1 && addrParts[0] != "" {
|
||||
addrParts = []string{"tcp", addrParts[0]}
|
||||
}
|
||||
if addrParts[0] != "tcp" {
|
||||
return "", errors.New("dnet currently only supports tcp transport")
|
||||
}
|
||||
|
||||
return parseTCPAddr(addrParts[1], defaultTCPHost)
|
||||
}
|
||||
|
||||
// parseTCPAddr parses and validates that the specified address is a valid TCP
|
||||
// address. It returns a formatted TCP address, either using the address parsed
|
||||
// from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
|
||||
// tryAddr is expected to have already been Trim()'d
|
||||
// defaultAddr must be in the full `tcp://host:port` form
|
||||
func parseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
|
||||
if tryAddr == "" || tryAddr == "tcp://" {
|
||||
return defaultAddr, nil
|
||||
}
|
||||
addr := strings.TrimPrefix(tryAddr, "tcp://")
|
||||
if strings.Contains(addr, "://") || addr == "" {
|
||||
return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
|
||||
}
|
||||
|
||||
defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
|
||||
defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
u, err := url.Parse("tcp://" + addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
// try port addition once
|
||||
host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort))
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
host = defaultHost
|
||||
}
|
||||
if port == "" {
|
||||
port = defaultPort
|
||||
}
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil && p == 0 {
|
||||
return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
|
||||
}
|
||||
|
||||
func (d *dnetConnection) httpCall(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
|
||||
var in io.Reader
|
||||
in, err := encodeData(data)
|
||||
if err != nil {
|
||||
return nil, nil, -1, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, path, in)
|
||||
if err != nil {
|
||||
return nil, nil, -1, err
|
||||
}
|
||||
|
||||
setupRequestHeaders(method, data, req, headers)
|
||||
|
||||
req.URL.Host = d.addr
|
||||
req.URL.Scheme = "http"
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
statusCode := -1
|
||||
if resp != nil {
|
||||
statusCode = resp.StatusCode
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, statusCode, fmt.Errorf("error when trying to connect: %v", err)
|
||||
}
|
||||
|
||||
if statusCode < 200 || statusCode >= 400 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, statusCode, err
|
||||
}
|
||||
return nil, nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
|
||||
}
|
||||
|
||||
return resp.Body, resp.Header, statusCode, nil
|
||||
}
|
||||
|
||||
func setupRequestHeaders(method string, data interface{}, req *http.Request, headers map[string][]string) {
|
||||
if data != nil {
|
||||
if headers == nil {
|
||||
headers = make(map[string][]string)
|
||||
}
|
||||
headers["Content-Type"] = []string{"application/json"}
|
||||
}
|
||||
|
||||
expectedPayload := (method == "POST" || method == "PUT")
|
||||
|
||||
if expectedPayload && req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
req.Header[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func encodeData(data interface{}) (*bytes.Buffer, error) {
|
||||
params := bytes.NewBuffer(nil)
|
||||
if data != nil {
|
||||
if err := json.NewEncoder(params).Encode(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func ipamOption(bridgeName string) libnetwork.NetworkOption {
|
||||
if nws, _, err := netutils.ElectInterfaceAddresses(bridgeName); err == nil {
|
||||
ipamV4Conf := &libnetwork.IpamConf{PreferredPool: nws[0].String()}
|
||||
hip, _ := types.GetHostPartIP(nws[0].IP, nws[0].Mask)
|
||||
if hip.IsGlobalUnicast() {
|
||||
ipamV4Conf.Gateway = nws[0].IP.String()
|
||||
}
|
||||
return libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
19
libnetwork/cmd/dnet/dnet_linux.go
Normal file
19
libnetwork/cmd/dnet/dnet_linux.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
psignal "github.com/docker/docker/pkg/signal"
|
||||
)
|
||||
|
||||
func setupDumpStackTrap() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGUSR1)
|
||||
go func() {
|
||||
for range c {
|
||||
psignal.DumpStacks("")
|
||||
}
|
||||
}()
|
||||
}
|
27
libnetwork/cmd/dnet/dnet_windows.go
Normal file
27
libnetwork/cmd/dnet/dnet_windows.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Copied over from docker/daemon/debugtrap_windows.go
|
||||
func setupDumpStackTrap() {
|
||||
go func() {
|
||||
sa := windows.SecurityAttributes{
|
||||
Length: 0,
|
||||
}
|
||||
ev, _ := windows.UTF16PtrFromString("Global\\docker-daemon-" + fmt.Sprint(os.Getpid()))
|
||||
if h, _ := windows.CreateEvent(&sa, 0, 0, ev); h != 0 {
|
||||
logrus.Debugf("Stackdump - waiting signal at %d", *ev)
|
||||
for {
|
||||
windows.WaitForSingleObject(h, windows.INFINITE)
|
||||
signal.DumpStacks("")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
87
libnetwork/cmd/dnet/flags.go
Normal file
87
libnetwork/cmd/dnet/flags.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
dnetFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "d, -daemon",
|
||||
Usage: "Enable daemon mode",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "H, -host",
|
||||
Value: "",
|
||||
Usage: "Daemon socket to connect to",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "l, -log-level",
|
||||
Value: "info",
|
||||
Usage: "Set the logging level",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "D, -debug",
|
||||
Usage: "Enable debug mode",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "c, -cfg-file",
|
||||
Value: "/etc/default/libnetwork.toml",
|
||||
Usage: "Configuration file",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func processFlags(c *cli.Context) error {
|
||||
var err error
|
||||
|
||||
if c.String("l") != "" {
|
||||
lvl, err := logrus.ParseLevel(c.String("l"))
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to parse logging level: %s\n", c.String("l"))
|
||||
os.Exit(1)
|
||||
}
|
||||
logrus.SetLevel(lvl)
|
||||
} else {
|
||||
logrus.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
|
||||
if c.Bool("D") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
hostFlag := c.String("H")
|
||||
if hostFlag == "" {
|
||||
defaultHost := os.Getenv("DNET_HOST")
|
||||
if defaultHost == "" {
|
||||
// TODO : Add UDS support
|
||||
defaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)
|
||||
}
|
||||
hostFlag = defaultHost
|
||||
}
|
||||
|
||||
epConn, err = newDnetConnection(hostFlag)
|
||||
if err != nil {
|
||||
if c.Bool("d") {
|
||||
logrus.Error(err)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if c.Bool("d") {
|
||||
err = epConn.dnetDaemon(c.String("c"))
|
||||
if err != nil {
|
||||
logrus.Errorf("dnet Daemon exited with an error : %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
16
libnetwork/cmd/dnet/libnetwork.toml
Executable file
16
libnetwork/cmd/dnet/libnetwork.toml
Executable file
|
@ -0,0 +1,16 @@
|
|||
title = "LibNetwork Configuration file"
|
||||
|
||||
[daemon]
|
||||
debug = false
|
||||
[cluster]
|
||||
discovery = "consul://localhost:8500"
|
||||
Address = "1.1.1.1"
|
||||
Heartbeat = 20
|
||||
[datastore]
|
||||
embedded = false
|
||||
[datastore.client]
|
||||
provider = "consul"
|
||||
Address = "localhost:8500"
|
||||
[orchestration]
|
||||
agent = true
|
||||
peer="2.2.2.2"
|
9
libnetwork/cmd/networkdb-test/Dockerfile
Normal file
9
libnetwork/cmd/networkdb-test/Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
|||
FROM alpine
|
||||
|
||||
RUN apk --no-cache add curl
|
||||
|
||||
COPY testMain /app/
|
||||
|
||||
WORKDIR app
|
||||
|
||||
ENTRYPOINT ["/app/testMain"]
|
15
libnetwork/cmd/networkdb-test/README
Normal file
15
libnetwork/cmd/networkdb-test/README
Normal file
|
@ -0,0 +1,15 @@
|
|||
SERVER
|
||||
|
||||
cd test/networkdb
|
||||
env GOOS=linux go build -v testMain.go && docker build -t dockereng/e2e-networkdb .
|
||||
(only for testkit case) docker push dockereng/e2e-networkdb
|
||||
|
||||
Run server: docker service create --name testdb --network net1 --replicas 3 --env TASK_ID="{{.Task.ID}}" -p mode=host,target=8000 dockereng/e2e-networkdb server 8000
|
||||
|
||||
CLIENT
|
||||
|
||||
cd test/networkdb
|
||||
Join cluster: docker run -it --network net1 dockereng/e2e-networkdb client join testdb 8000
|
||||
Join network: docker run -it --network net1 dockereng/e2e-networkdb client join-network testdb 8000 test
|
||||
Run test: docker run -it --network net1 dockereng/e2e-networkdb client write-delete-unique-keys testdb 8000 test tableBla 3 10
|
||||
check table: curl "localhost:32768/gettable?nid=test&tname=table_name"
|
861
libnetwork/cmd/networkdb-test/dbclient/ndbClient.go
Normal file
861
libnetwork/cmd/networkdb-test/dbclient/ndbClient.go
Normal file
|
@ -0,0 +1,861 @@
|
|||
package dbclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var servicePort string
|
||||
|
||||
const totalWrittenKeys string = "totalKeys"
|
||||
|
||||
type resultTuple struct {
|
||||
id string
|
||||
result int
|
||||
}
|
||||
|
||||
func httpGetFatalError(ip, port, path string) {
|
||||
body, err := httpGet(ip, port, path)
|
||||
if err != nil || !strings.Contains(string(body), "OK") {
|
||||
log.Fatalf("[%s] error %s %s", path, err, body)
|
||||
}
|
||||
}
|
||||
|
||||
func httpGet(ip, port, path string) ([]byte, error) {
|
||||
resp, err := http.Get("http://" + ip + ":" + port + path)
|
||||
if err != nil {
|
||||
logrus.Errorf("httpGet error:%s", err)
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
func joinCluster(ip, port string, members []string, doneCh chan resultTuple) {
|
||||
httpGetFatalError(ip, port, "/join?members="+strings.Join(members, ","))
|
||||
|
||||
if doneCh != nil {
|
||||
doneCh <- resultTuple{id: ip, result: 0}
|
||||
}
|
||||
}
|
||||
|
||||
func joinNetwork(ip, port, network string, doneCh chan resultTuple) {
|
||||
httpGetFatalError(ip, port, "/joinnetwork?nid="+network)
|
||||
|
||||
if doneCh != nil {
|
||||
doneCh <- resultTuple{id: ip, result: 0}
|
||||
}
|
||||
}
|
||||
|
||||
func leaveNetwork(ip, port, network string, doneCh chan resultTuple) {
|
||||
httpGetFatalError(ip, port, "/leavenetwork?nid="+network)
|
||||
|
||||
if doneCh != nil {
|
||||
doneCh <- resultTuple{id: ip, result: 0}
|
||||
}
|
||||
}
|
||||
|
||||
func writeTableKey(ip, port, networkName, tableName, key string) {
|
||||
createPath := "/createentry?unsafe&nid=" + networkName + "&tname=" + tableName + "&value=v&key="
|
||||
httpGetFatalError(ip, port, createPath+key)
|
||||
}
|
||||
|
||||
func deleteTableKey(ip, port, networkName, tableName, key string) {
|
||||
deletePath := "/deleteentry?nid=" + networkName + "&tname=" + tableName + "&key="
|
||||
httpGetFatalError(ip, port, deletePath+key)
|
||||
}
|
||||
|
||||
func clusterPeersNumber(ip, port string, doneCh chan resultTuple) {
|
||||
body, err := httpGet(ip, port, "/clusterpeers")
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("clusterPeers %s there was an error: %s", ip, err)
|
||||
doneCh <- resultTuple{id: ip, result: -1}
|
||||
return
|
||||
}
|
||||
peersRegexp := regexp.MustCompile(`total entries: ([0-9]+)`)
|
||||
peersNum, _ := strconv.Atoi(peersRegexp.FindStringSubmatch(string(body))[1])
|
||||
|
||||
doneCh <- resultTuple{id: ip, result: peersNum}
|
||||
}
|
||||
|
||||
func networkPeersNumber(ip, port, networkName string, doneCh chan resultTuple) {
|
||||
body, err := httpGet(ip, port, "/networkpeers?nid="+networkName)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("networkPeersNumber %s there was an error: %s", ip, err)
|
||||
doneCh <- resultTuple{id: ip, result: -1}
|
||||
return
|
||||
}
|
||||
peersRegexp := regexp.MustCompile(`total entries: ([0-9]+)`)
|
||||
peersNum, _ := strconv.Atoi(peersRegexp.FindStringSubmatch(string(body))[1])
|
||||
|
||||
doneCh <- resultTuple{id: ip, result: peersNum}
|
||||
}
|
||||
|
||||
func dbTableEntriesNumber(ip, port, networkName, tableName string, doneCh chan resultTuple) {
|
||||
body, err := httpGet(ip, port, "/gettable?nid="+networkName+"&tname="+tableName)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("tableEntriesNumber %s there was an error: %s", ip, err)
|
||||
doneCh <- resultTuple{id: ip, result: -1}
|
||||
return
|
||||
}
|
||||
elementsRegexp := regexp.MustCompile(`total entries: ([0-9]+)`)
|
||||
entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1])
|
||||
doneCh <- resultTuple{id: ip, result: entriesNum}
|
||||
}
|
||||
|
||||
func dbQueueLength(ip, port, networkName string, doneCh chan resultTuple) {
|
||||
body, err := httpGet(ip, port, "/networkstats?nid="+networkName)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("queueLength %s there was an error: %s", ip, err)
|
||||
doneCh <- resultTuple{id: ip, result: -1}
|
||||
return
|
||||
}
|
||||
elementsRegexp := regexp.MustCompile(`qlen: ([0-9]+)`)
|
||||
entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1])
|
||||
doneCh <- resultTuple{id: ip, result: entriesNum}
|
||||
}
|
||||
|
||||
func clientWatchTable(ip, port, networkName, tableName string, doneCh chan resultTuple) {
|
||||
httpGetFatalError(ip, port, "/watchtable?nid="+networkName+"&tname="+tableName)
|
||||
if doneCh != nil {
|
||||
doneCh <- resultTuple{id: ip, result: 0}
|
||||
}
|
||||
}
|
||||
|
||||
func clientTableEntriesNumber(ip, port, networkName, tableName string, doneCh chan resultTuple) {
|
||||
body, err := httpGet(ip, port, "/watchedtableentries?nid="+networkName+"&tname="+tableName)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("clientTableEntriesNumber %s there was an error: %s", ip, err)
|
||||
doneCh <- resultTuple{id: ip, result: -1}
|
||||
return
|
||||
}
|
||||
elementsRegexp := regexp.MustCompile(`total elements: ([0-9]+)`)
|
||||
entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1])
|
||||
doneCh <- resultTuple{id: ip, result: entriesNum}
|
||||
}
|
||||
|
||||
func writeKeysNumber(ip, port, networkName, tableName, key string, number int, doneCh chan resultTuple) {
|
||||
x := 0
|
||||
for ; x < number; x++ {
|
||||
k := key + strconv.Itoa(x)
|
||||
// write key
|
||||
writeTableKey(ip, port, networkName, tableName, k)
|
||||
}
|
||||
doneCh <- resultTuple{id: ip, result: x}
|
||||
}
|
||||
|
||||
func deleteKeysNumber(ip, port, networkName, tableName, key string, number int, doneCh chan resultTuple) {
|
||||
x := 0
|
||||
for ; x < number; x++ {
|
||||
k := key + strconv.Itoa(x)
|
||||
// write key
|
||||
deleteTableKey(ip, port, networkName, tableName, k)
|
||||
}
|
||||
doneCh <- resultTuple{id: ip, result: x}
|
||||
}
|
||||
|
||||
func writeUniqueKeys(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) {
|
||||
for x := 0; ; x++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
doneCh <- resultTuple{id: ip, result: x}
|
||||
return
|
||||
default:
|
||||
k := key + strconv.Itoa(x)
|
||||
// write key
|
||||
writeTableKey(ip, port, networkName, tableName, k)
|
||||
// give time to send out key writes
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeDeleteUniqueKeys(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) {
|
||||
for x := 0; ; x++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
doneCh <- resultTuple{id: ip, result: x}
|
||||
return
|
||||
default:
|
||||
k := key + strconv.Itoa(x)
|
||||
// write key
|
||||
writeTableKey(ip, port, networkName, tableName, k)
|
||||
// give time to send out key writes
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// delete key
|
||||
deleteTableKey(ip, port, networkName, tableName, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeDeleteLeaveJoin(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) {
|
||||
for x := 0; ; x++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
doneCh <- resultTuple{id: ip, result: x}
|
||||
return
|
||||
default:
|
||||
k := key + strconv.Itoa(x)
|
||||
// write key
|
||||
writeTableKey(ip, port, networkName, tableName, k)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// delete key
|
||||
deleteTableKey(ip, port, networkName, tableName, k)
|
||||
// give some time
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// leave network
|
||||
leaveNetwork(ip, port, networkName, nil)
|
||||
// join network
|
||||
joinNetwork(ip, port, networkName, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ready(ip, port string, doneCh chan resultTuple) {
|
||||
for {
|
||||
body, err := httpGet(ip, port, "/ready")
|
||||
if err != nil || !strings.Contains(string(body), "OK") {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
// success
|
||||
break
|
||||
}
|
||||
// notify the completion
|
||||
doneCh <- resultTuple{id: ip, result: 0}
|
||||
}
|
||||
|
||||
func checkTable(ctx context.Context, ips []string, port, networkName, tableName string, expectedEntries int, fn func(string, string, string, string, chan resultTuple)) (opTime time.Duration) {
|
||||
startTime := time.Now().UnixNano()
|
||||
var successTime int64
|
||||
|
||||
// Loop for 2 minutes to guarantee that the result is stable
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Validate test success, if the time is set means that all the tables are empty
|
||||
if successTime != 0 {
|
||||
opTime = time.Duration(successTime-startTime) / time.Millisecond
|
||||
logrus.Infof("Check table passed, the cluster converged in %d msec", opTime)
|
||||
return
|
||||
}
|
||||
log.Fatal("Test failed, there is still entries in the tables of the nodes")
|
||||
default:
|
||||
logrus.Infof("Checking table %s expected %d", tableName, expectedEntries)
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
for _, ip := range ips {
|
||||
go fn(ip, servicePort, networkName, tableName, doneCh)
|
||||
}
|
||||
|
||||
nodesWithCorrectEntriesNum := 0
|
||||
for i := len(ips); i > 0; i-- {
|
||||
tableEntries := <-doneCh
|
||||
logrus.Infof("Node %s has %d entries", tableEntries.id, tableEntries.result)
|
||||
if tableEntries.result == expectedEntries {
|
||||
nodesWithCorrectEntriesNum++
|
||||
}
|
||||
}
|
||||
close(doneCh)
|
||||
if nodesWithCorrectEntriesNum == len(ips) {
|
||||
if successTime == 0 {
|
||||
successTime = time.Now().UnixNano()
|
||||
logrus.Infof("Success after %d msec", time.Duration(successTime-startTime)/time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
successTime = 0
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func waitWriters(parallelWriters int, mustWrite bool, doneCh chan resultTuple) map[string]int {
|
||||
var totalKeys int
|
||||
resultTable := make(map[string]int)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
logrus.Infof("Waiting for %d workers", parallelWriters-i)
|
||||
workerReturn := <-doneCh
|
||||
totalKeys += workerReturn.result
|
||||
if mustWrite && workerReturn.result == 0 {
|
||||
log.Fatalf("The worker %s did not write any key %d == 0", workerReturn.id, workerReturn.result)
|
||||
}
|
||||
if !mustWrite && workerReturn.result != 0 {
|
||||
log.Fatalf("The worker %s was supposed to return 0 instead %d != 0", workerReturn.id, workerReturn.result)
|
||||
}
|
||||
if mustWrite {
|
||||
resultTable[workerReturn.id] = workerReturn.result
|
||||
logrus.Infof("The worker %s wrote %d keys", workerReturn.id, workerReturn.result)
|
||||
}
|
||||
}
|
||||
resultTable[totalWrittenKeys] = totalKeys
|
||||
return resultTable
|
||||
}
|
||||
|
||||
// ready
|
||||
func doReady(ips []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
// check all the nodes
|
||||
for _, ip := range ips {
|
||||
go ready(ip, servicePort, doneCh)
|
||||
}
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
<-doneCh
|
||||
}
|
||||
close(doneCh)
|
||||
}
|
||||
|
||||
// join
|
||||
func doJoin(ips []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
// check all the nodes
|
||||
for i, ip := range ips {
|
||||
members := append([]string(nil), ips[:i]...)
|
||||
members = append(members, ips[i+1:]...)
|
||||
go joinCluster(ip, servicePort, members, doneCh)
|
||||
}
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
<-doneCh
|
||||
}
|
||||
close(doneCh)
|
||||
}
|
||||
|
||||
// cluster-peers expectedNumberPeers maxRetry
|
||||
func doClusterPeers(ips []string, args []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
expectedPeers, _ := strconv.Atoi(args[0])
|
||||
maxRetry, _ := strconv.Atoi(args[1])
|
||||
for retry := 0; retry < maxRetry; retry++ {
|
||||
// check all the nodes
|
||||
for _, ip := range ips {
|
||||
go clusterPeersNumber(ip, servicePort, doneCh)
|
||||
}
|
||||
var failed bool
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
node := <-doneCh
|
||||
if node.result != expectedPeers {
|
||||
failed = true
|
||||
if retry == maxRetry-1 {
|
||||
log.Fatalf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
|
||||
} else {
|
||||
logrus.Warnf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
// check if needs retry
|
||||
if !failed {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(doneCh)
|
||||
}
|
||||
|
||||
// join-network networkName
|
||||
func doJoinNetwork(ips []string, args []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
// check all the nodes
|
||||
for _, ip := range ips {
|
||||
go joinNetwork(ip, servicePort, args[0], doneCh)
|
||||
}
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
<-doneCh
|
||||
}
|
||||
close(doneCh)
|
||||
}
|
||||
|
||||
// leave-network networkName
|
||||
func doLeaveNetwork(ips []string, args []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
// check all the nodes
|
||||
for _, ip := range ips {
|
||||
go leaveNetwork(ip, servicePort, args[0], doneCh)
|
||||
}
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
<-doneCh
|
||||
}
|
||||
close(doneCh)
|
||||
}
|
||||
|
||||
// network-peers networkName expectedNumberPeers maxRetry
|
||||
func doNetworkPeers(ips []string, args []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
networkName := args[0]
|
||||
expectedPeers, _ := strconv.Atoi(args[1])
|
||||
maxRetry, _ := strconv.Atoi(args[2])
|
||||
for retry := 0; retry < maxRetry; retry++ {
|
||||
// check all the nodes
|
||||
for _, ip := range ips {
|
||||
go networkPeersNumber(ip, servicePort, networkName, doneCh)
|
||||
}
|
||||
var failed bool
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
node := <-doneCh
|
||||
if node.result != expectedPeers {
|
||||
failed = true
|
||||
if retry == maxRetry-1 {
|
||||
log.Fatalf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
|
||||
} else {
|
||||
logrus.Warnf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
// check if needs retry
|
||||
if !failed {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(doneCh)
|
||||
}
|
||||
|
||||
// network-stats-queue networkName <gt/lt> queueSize
|
||||
func doNetworkStatsQueue(ips []string, args []string) {
|
||||
doneCh := make(chan resultTuple, len(ips))
|
||||
networkName := args[0]
|
||||
comparison := args[1]
|
||||
size, _ := strconv.Atoi(args[2])
|
||||
|
||||
// check all the nodes
|
||||
for _, ip := range ips {
|
||||
go dbQueueLength(ip, servicePort, networkName, doneCh)
|
||||
}
|
||||
|
||||
var avgQueueSize int
|
||||
// wait for the readiness of all nodes
|
||||
for i := len(ips); i > 0; i-- {
|
||||
node := <-doneCh
|
||||
switch comparison {
|
||||
case "lt":
|
||||
if node.result > size {
|
||||
log.Fatalf("Expected queue size from %s to be %d < %d", node.id, node.result, size)
|
||||
}
|
||||
case "gt":
|
||||
if node.result < size {
|
||||
log.Fatalf("Expected queue size from %s to be %d > %d", node.id, node.result, size)
|
||||
}
|
||||
default:
|
||||
log.Fatal("unknown comparison operator")
|
||||
}
|
||||
avgQueueSize += node.result
|
||||
}
|
||||
close(doneCh)
|
||||
avgQueueSize /= len(ips)
|
||||
fmt.Fprintf(os.Stderr, "doNetworkStatsQueue succeeded with avg queue:%d", avgQueueSize)
|
||||
}
|
||||
|
||||
// write-keys networkName tableName parallelWriters numberOfKeysEach
|
||||
func doWriteKeys(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
numberOfKeys, _ := strconv.Atoi(args[3])
|
||||
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
// Enable watch of tables from clients
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
defer close(doneCh)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeKeysNumber(ips[i], servicePort, networkName, tableName, key, numberOfKeys, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keyMap[totalWrittenKeys], dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteKeys succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
// delete-keys networkName tableName parallelWriters numberOfKeysEach
|
||||
func doDeleteKeys(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
numberOfKeys, _ := strconv.Atoi(args[3])
|
||||
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
// Enable watch of tables from clients
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
defer close(doneCh)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go deleteKeysNumber(ips[i], servicePort, networkName, tableName, key, numberOfKeys, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doDeletekeys succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
// write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec
|
||||
func doWriteDeleteUniqueKeys(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
writeTimeSec, _ := strconv.Atoi(args[3])
|
||||
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
// Enable watch of tables from clients
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeDeleteUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
cancel()
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
opDBTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
|
||||
cancel()
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
|
||||
opClientTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, clientTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteDeleteUniqueKeys succeeded in %d msec and client %d msec", opDBTime, opClientTime)
|
||||
}
|
||||
|
||||
// write-unique-keys networkName tableName numParallelWriters writeTimeSec
|
||||
func doWriteUniqueKeys(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
writeTimeSec, _ := strconv.Atoi(args[3])
|
||||
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
// Enable watch of tables from clients
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
defer close(doneCh)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
cancel()
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keyMap[totalWrittenKeys], dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteUniqueKeys succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
// write-delete-leave-join networkName tableName numParallelWriters writeTimeSec
|
||||
func doWriteDeleteLeaveJoin(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
writeTimeSec, _ := strconv.Atoi(args[3])
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
defer close(doneCh)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeDeleteLeaveJoin(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
cancel()
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap["totalKeys"])
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteDeleteLeaveJoin succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
// write-delete-wait-leave-join networkName tableName numParallelWriters writeTimeSec
|
||||
func doWriteDeleteWaitLeaveJoin(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
writeTimeSec, _ := strconv.Atoi(args[3])
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
defer close(doneCh)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeDeleteUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
cancel()
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
// The writers will leave the network
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
logrus.Infof("worker leaveNetwork: %d on IP:%s", i, ips[i])
|
||||
go leaveNetwork(ips[i], servicePort, networkName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// Give some time
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// The writers will join the network
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
logrus.Infof("worker joinNetwork: %d on IP:%s", i, ips[i])
|
||||
go joinNetwork(ips[i], servicePort, networkName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteDeleteWaitLeaveJoin succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
// write-wait-leave networkName tableName numParallelWriters writeTimeSec
|
||||
func doWriteWaitLeave(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
writeTimeSec, _ := strconv.Atoi(args[3])
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
defer close(doneCh)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
cancel()
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
// The writers will leave the network
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
logrus.Infof("worker leaveNetwork: %d on IP:%s", i, ips[i])
|
||||
go leaveNetwork(ips[i], servicePort, networkName, doneCh)
|
||||
}
|
||||
waitWriters(parallelWriters, false, doneCh)
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteLeaveJoin succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
// write-wait-leave-join networkName tableName numParallelWriters writeTimeSec numParallelLeaver
|
||||
func doWriteWaitLeaveJoin(ips []string, args []string) {
|
||||
networkName := args[0]
|
||||
tableName := args[1]
|
||||
parallelWriters, _ := strconv.Atoi(args[2])
|
||||
writeTimeSec, _ := strconv.Atoi(args[3])
|
||||
parallelLeaver, _ := strconv.Atoi(args[4])
|
||||
|
||||
// Start parallel writers that will create and delete unique keys
|
||||
doneCh := make(chan resultTuple, parallelWriters)
|
||||
defer close(doneCh)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
|
||||
for i := 0; i < parallelWriters; i++ {
|
||||
key := "key-" + strconv.Itoa(i) + "-"
|
||||
logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i])
|
||||
go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
|
||||
}
|
||||
|
||||
// Sync with all the writers
|
||||
keyMap := waitWriters(parallelWriters, true, doneCh)
|
||||
cancel()
|
||||
logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
|
||||
|
||||
keysExpected := keyMap[totalWrittenKeys]
|
||||
// The Leavers will leave the network
|
||||
for i := 0; i < parallelLeaver; i++ {
|
||||
logrus.Infof("worker leaveNetwork: %d on IP:%s", i, ips[i])
|
||||
go leaveNetwork(ips[i], servicePort, networkName, doneCh)
|
||||
// Once a node leave all the keys written previously will be deleted, so the expected keys will consider that as removed
|
||||
keysExpected -= keyMap[ips[i]]
|
||||
}
|
||||
waitWriters(parallelLeaver, false, doneCh)
|
||||
|
||||
// Give some time
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// The writers will join the network
|
||||
for i := 0; i < parallelLeaver; i++ {
|
||||
logrus.Infof("worker joinNetwork: %d on IP:%s", i, ips[i])
|
||||
go joinNetwork(ips[i], servicePort, networkName, doneCh)
|
||||
}
|
||||
waitWriters(parallelLeaver, false, doneCh)
|
||||
|
||||
// check table entries for 2 minutes
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keysExpected, dbTableEntriesNumber)
|
||||
cancel()
|
||||
fmt.Fprintf(os.Stderr, "doWriteWaitLeaveJoin succeeded in %d msec", opTime)
|
||||
}
|
||||
|
||||
var cmdArgChec = map[string]int{
|
||||
"debug": 0,
|
||||
"fail": 0,
|
||||
"ready": 2,
|
||||
"join": 2,
|
||||
"leave": 2,
|
||||
"join-network": 3,
|
||||
"leave-network": 3,
|
||||
"cluster-peers": 5,
|
||||
"network-peers": 5,
|
||||
"write-delete-unique-keys": 7,
|
||||
}
|
||||
|
||||
// Client is a client
|
||||
func Client(args []string) {
|
||||
logrus.Infof("[CLIENT] Starting with arguments %v", args)
|
||||
command := args[0]
|
||||
|
||||
if len(args) < cmdArgChec[command] {
|
||||
log.Fatalf("Command %s requires %d arguments, passed %d, aborting...", command, cmdArgChec[command], len(args))
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "debug":
|
||||
time.Sleep(1 * time.Hour)
|
||||
os.Exit(0)
|
||||
case "fail":
|
||||
log.Fatalf("Test error condition with message: error error error")
|
||||
}
|
||||
|
||||
serviceName := args[1]
|
||||
ips, _ := net.LookupHost("tasks." + serviceName)
|
||||
logrus.Infof("got the ips %v", ips)
|
||||
if len(ips) == 0 {
|
||||
log.Fatalf("Cannot resolve any IP for the service tasks.%s", serviceName)
|
||||
}
|
||||
servicePort = args[2]
|
||||
commandArgs := args[3:]
|
||||
logrus.Infof("Executing %s with args:%v", command, commandArgs)
|
||||
switch command {
|
||||
case "ready":
|
||||
doReady(ips)
|
||||
case "join":
|
||||
doJoin(ips)
|
||||
case "leave":
|
||||
|
||||
case "cluster-peers":
|
||||
// cluster-peers maxRetry
|
||||
doClusterPeers(ips, commandArgs)
|
||||
|
||||
case "join-network":
|
||||
// join-network networkName
|
||||
doJoinNetwork(ips, commandArgs)
|
||||
case "leave-network":
|
||||
// leave-network networkName
|
||||
doLeaveNetwork(ips, commandArgs)
|
||||
case "network-peers":
|
||||
// network-peers networkName expectedNumberPeers maxRetry
|
||||
doNetworkPeers(ips, commandArgs)
|
||||
// case "network-stats-entries":
|
||||
// // network-stats-entries networkName maxRetry
|
||||
// doNetworkPeers(ips, commandArgs)
|
||||
case "network-stats-queue":
|
||||
// network-stats-queue networkName <lt/gt> queueSize
|
||||
doNetworkStatsQueue(ips, commandArgs)
|
||||
|
||||
case "write-keys":
|
||||
// write-keys networkName tableName parallelWriters numberOfKeysEach
|
||||
doWriteKeys(ips, commandArgs)
|
||||
case "delete-keys":
|
||||
// delete-keys networkName tableName parallelWriters numberOfKeysEach
|
||||
doDeleteKeys(ips, commandArgs)
|
||||
case "write-unique-keys":
|
||||
// write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec
|
||||
doWriteUniqueKeys(ips, commandArgs)
|
||||
case "write-delete-unique-keys":
|
||||
// write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec
|
||||
doWriteDeleteUniqueKeys(ips, commandArgs)
|
||||
case "write-delete-leave-join":
|
||||
// write-delete-leave-join networkName tableName numParallelWriters writeTimeSec
|
||||
doWriteDeleteLeaveJoin(ips, commandArgs)
|
||||
case "write-delete-wait-leave-join":
|
||||
// write-delete-wait-leave-join networkName tableName numParallelWriters writeTimeSec
|
||||
doWriteDeleteWaitLeaveJoin(ips, commandArgs)
|
||||
case "write-wait-leave":
|
||||
// write-wait-leave networkName tableName numParallelWriters writeTimeSec
|
||||
doWriteWaitLeave(ips, commandArgs)
|
||||
case "write-wait-leave-join":
|
||||
// write-wait-leave networkName tableName numParallelWriters writeTimeSec
|
||||
doWriteWaitLeaveJoin(ips, commandArgs)
|
||||
default:
|
||||
log.Fatalf("Command %s not recognized", command)
|
||||
}
|
||||
}
|
111
libnetwork/cmd/networkdb-test/dbserver/ndbServer.go
Normal file
111
libnetwork/cmd/networkdb-test/dbserver/ndbServer.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package dbserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/libnetwork/cmd/networkdb-test/dummyclient"
|
||||
"github.com/docker/docker/libnetwork/diagnostic"
|
||||
"github.com/docker/docker/libnetwork/networkdb"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var nDB *networkdb.NetworkDB
|
||||
var server *diagnostic.Server
|
||||
var ipAddr string
|
||||
|
||||
var testerPaths2Func = map[string]diagnostic.HTTPHandlerFunc{
|
||||
"/myip": ipaddress,
|
||||
}
|
||||
|
||||
func ipaddress(ctx interface{}, w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "%s\n", ipAddr)
|
||||
}
|
||||
|
||||
// Server starts the server
|
||||
func Server(args []string) {
|
||||
logrus.Infof("[SERVER] Starting with arguments %v", args)
|
||||
if len(args) < 1 {
|
||||
log.Fatal("Port number is a mandatory argument, aborting...")
|
||||
}
|
||||
port, _ := strconv.Atoi(args[0])
|
||||
var localNodeName string
|
||||
var ok bool
|
||||
if localNodeName, ok = os.LookupEnv("TASK_ID"); !ok {
|
||||
log.Fatal("TASK_ID environment variable not set, aborting...")
|
||||
}
|
||||
logrus.Infof("[SERVER] Starting node %s on port %d", localNodeName, port)
|
||||
|
||||
ip, err := getIPInterface("eth0")
|
||||
if err != nil {
|
||||
logrus.Errorf("%s There was a problem with the IP %s\n", localNodeName, err)
|
||||
return
|
||||
}
|
||||
ipAddr = ip
|
||||
logrus.Infof("%s uses IP %s\n", localNodeName, ipAddr)
|
||||
|
||||
server = diagnostic.New()
|
||||
server.Init()
|
||||
conf := networkdb.DefaultConfig()
|
||||
conf.Hostname = localNodeName
|
||||
conf.AdvertiseAddr = ipAddr
|
||||
conf.BindAddr = ipAddr
|
||||
nDB, err = networkdb.New(conf)
|
||||
if err != nil {
|
||||
logrus.Infof("%s error in the DB init %s\n", localNodeName, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Register network db handlers
|
||||
server.RegisterHandler(nDB, networkdb.NetDbPaths2Func)
|
||||
server.RegisterHandler(nil, testerPaths2Func)
|
||||
server.RegisterHandler(nDB, dummyclient.DummyClientPaths2Func)
|
||||
server.EnableDiagnostic("", port)
|
||||
// block here
|
||||
select {}
|
||||
}
|
||||
|
||||
func getIPInterface(name string) (string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if iface.Name != name {
|
||||
continue // not the name specified
|
||||
}
|
||||
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
return "", errors.New("Interfaces is down")
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
return ip.String(), nil
|
||||
}
|
||||
return "", errors.New("Interfaces does not have a valid IPv4")
|
||||
}
|
||||
return "", errors.New("Interface not found")
|
||||
}
|
120
libnetwork/cmd/networkdb-test/dummyclient/dummyClient.go
Normal file
120
libnetwork/cmd/networkdb-test/dummyclient/dummyClient.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package dummyclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/libnetwork/diagnostic"
|
||||
"github.com/docker/docker/libnetwork/networkdb"
|
||||
events "github.com/docker/go-events"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DummyClientPaths2Func exported paths for the client
|
||||
var DummyClientPaths2Func = map[string]diagnostic.HTTPHandlerFunc{
|
||||
"/watchtable": watchTable,
|
||||
"/watchedtableentries": watchTableEntries,
|
||||
}
|
||||
|
||||
const (
|
||||
missingParameter = "missing parameter"
|
||||
)
|
||||
|
||||
type tableHandler struct {
|
||||
cancelWatch func()
|
||||
entries map[string]string
|
||||
}
|
||||
|
||||
var clientWatchTable = map[string]tableHandler{}
|
||||
|
||||
func watchTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm() // nolint:errcheck
|
||||
diagnostic.DebugHTTPForm(r)
|
||||
if len(r.Form["tname"]) < 1 {
|
||||
rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name", r.URL.Path))
|
||||
diagnostic.HTTPReply(w, rsp, &diagnostic.JSONOutput{}) // nolint:errcheck
|
||||
return
|
||||
}
|
||||
|
||||
tableName := r.Form["tname"][0]
|
||||
if _, ok := clientWatchTable[tableName]; ok {
|
||||
fmt.Fprintf(w, "OK\n")
|
||||
return
|
||||
}
|
||||
|
||||
nDB, ok := ctx.(*networkdb.NetworkDB)
|
||||
if ok {
|
||||
ch, cancel := nDB.Watch(tableName, "", "")
|
||||
clientWatchTable[tableName] = tableHandler{cancelWatch: cancel, entries: make(map[string]string)}
|
||||
go handleTableEvents(tableName, ch)
|
||||
|
||||
fmt.Fprintf(w, "OK\n")
|
||||
}
|
||||
}
|
||||
|
||||
func watchTableEntries(ctx interface{}, w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm() // nolint:errcheck
|
||||
diagnostic.DebugHTTPForm(r)
|
||||
if len(r.Form["tname"]) < 1 {
|
||||
rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name", r.URL.Path))
|
||||
diagnostic.HTTPReply(w, rsp, &diagnostic.JSONOutput{}) // nolint:errcheck
|
||||
return
|
||||
}
|
||||
|
||||
tableName := r.Form["tname"][0]
|
||||
table, ok := clientWatchTable[tableName]
|
||||
if !ok {
|
||||
fmt.Fprintf(w, "Table %s not watched\n", tableName)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "total elements: %d\n", len(table.entries))
|
||||
i := 0
|
||||
for k, v := range table.entries {
|
||||
fmt.Fprintf(w, "%d) k:`%s` -> v:`%s`\n", i, k, v)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func handleTableEvents(tableName string, ch *events.Channel) {
|
||||
var (
|
||||
// nid string
|
||||
eid string
|
||||
value []byte
|
||||
isAdd bool
|
||||
)
|
||||
|
||||
logrus.Infof("Started watching table:%s", tableName)
|
||||
for {
|
||||
select {
|
||||
case <-ch.Done():
|
||||
logrus.Infof("End watching %s", tableName)
|
||||
return
|
||||
|
||||
case evt := <-ch.C:
|
||||
logrus.Infof("Recevied new event on:%s", tableName)
|
||||
switch event := evt.(type) {
|
||||
case networkdb.CreateEvent:
|
||||
// nid = event.NetworkID
|
||||
eid = event.Key
|
||||
value = event.Value
|
||||
isAdd = true
|
||||
case networkdb.DeleteEvent:
|
||||
// nid = event.NetworkID
|
||||
eid = event.Key
|
||||
value = event.Value
|
||||
isAdd = false
|
||||
default:
|
||||
log.Fatalf("Unexpected table event = %#v", event)
|
||||
}
|
||||
if isAdd {
|
||||
// logrus.Infof("Add %s %s", tableName, eid)
|
||||
clientWatchTable[tableName].entries[eid] = string(value)
|
||||
} else {
|
||||
// logrus.Infof("Del %s %s", tableName, eid)
|
||||
delete(clientWatchTable[tableName].entries, eid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
libnetwork/cmd/networkdb-test/testMain.go
Normal file
28
libnetwork/cmd/networkdb-test/testMain.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/libnetwork/cmd/networkdb-test/dbclient"
|
||||
"github.com/docker/docker/libnetwork/cmd/networkdb-test/dbserver"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
formatter := &logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
}
|
||||
logrus.SetFormatter(formatter)
|
||||
logrus.Infof("Starting the image with these args: %v", os.Args)
|
||||
if len(os.Args) < 1 {
|
||||
log.Fatal("You need at least 1 argument [client/server]")
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "server":
|
||||
dbserver.Server(os.Args[2:])
|
||||
case "client":
|
||||
dbclient.Client(os.Args[2:])
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue