Merge pull request #46853 from akerouanton/libnet-ep-dns-names

libnet: Endpoint: remove isAnonymous & myAliases
This commit is contained in:
Sebastiaan van Stijn 2023-12-20 19:53:16 +01:00 committed by GitHub
commit 7bc56c5365
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 296 additions and 174 deletions

View file

@ -2530,6 +2530,21 @@ definitions:
example:
com.example.some-label: "some-value"
com.example.some-other-label: "some-other-value"
DNSNames:
description: |
List of all DNS names an endpoint has on a specific network. This
list is based on the container name, network aliases, container short
ID, and hostname.
These DNS names are non-fully qualified but can contain several dots.
You can get fully qualified DNS names by appending `.<network-name>`.
For instance, if container name is `my.ctr` and the network is named
`testnet`, `DNSNames` will contain `my.ctr` and the FQDN will be
`my.ctr.testnet`.
type: array
items:
type: string
example: ["foobar", "server_x", "server_y", "my.ctr"]
EndpointIPAMConfig:
description: |

View file

@ -13,7 +13,7 @@ type EndpointSettings struct {
// Configurations
IPAMConfig *EndpointIPAMConfig
Links []string
Aliases []string
Aliases []string // Aliases holds the list of extra, user-specified DNS names for this endpoint.
MacAddress string
// Operational data
NetworkID string
@ -25,6 +25,9 @@ type EndpointSettings struct {
GlobalIPv6Address string
GlobalIPv6PrefixLen int
DriverOpts map[string]string
// DNSNames holds all the (non fully qualified) DNS names associated to this endpoint. First entry is used to
// generate PTR records.
DNSNames []string
}
// Copy makes a deep copy of `EndpointSettings`
@ -43,6 +46,12 @@ func (es *EndpointSettings) Copy() *EndpointSettings {
aliases := make([]string, 0, len(es.Aliases))
epCopy.Aliases = append(aliases, es.Aliases...)
}
if len(es.DNSNames) > 0 {
epCopy.DNSNames = make([]string, len(es.DNSNames))
copy(epCopy.DNSNames, es.DNSNames)
}
return &epCopy
}

View file

@ -122,9 +122,8 @@ func (daemon *Daemon) Register(c *container.Container) error {
func (daemon *Daemon) newContainer(name string, operatingSystem string, config *containertypes.Config, hostConfig *containertypes.HostConfig, imgID image.ID, managed bool) (*container.Container, error) {
var (
id string
err error
noExplicitName = name == ""
id string
err error
)
id, name, err = daemon.generateIDAndName(name)
if err != nil {
@ -151,7 +150,7 @@ func (daemon *Daemon) newContainer(name string, operatingSystem string, config *
base.Config = config
base.HostConfig = &containertypes.HostConfig{}
base.ImageID = imgID
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
base.NetworkSettings = &network.Settings{}
base.Name = name
base.Driver = daemon.imageService.StorageDriver()
base.OS = operatingSystem

View file

@ -19,6 +19,7 @@ import (
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/internal/multierror"
"github.com/docker/docker/internal/sliceutil"
"github.com/docker/docker/libnetwork"
"github.com/docker/docker/libnetwork/netlabel"
"github.com/docker/docker/libnetwork/options"
@ -650,29 +651,7 @@ func cleanOperationalData(es *network.EndpointSettings) {
func (daemon *Daemon) updateNetworkConfig(container *container.Container, n *libnetwork.Network, endpointConfig *networktypes.EndpointSettings, updateSettings bool) error {
if containertypes.NetworkMode(n.Name()).IsUserDefined() {
addShortID := true
shortID := stringid.TruncateID(container.ID)
for _, alias := range endpointConfig.Aliases {
if alias == shortID {
addShortID = false
break
}
}
if addShortID {
endpointConfig.Aliases = append(endpointConfig.Aliases, shortID)
}
if container.Name != container.Config.Hostname {
addHostname := true
for _, alias := range endpointConfig.Aliases {
if alias == container.Config.Hostname {
addHostname = false
break
}
}
if addHostname {
endpointConfig.Aliases = append(endpointConfig.Aliases, container.Config.Hostname)
}
}
endpointConfig.DNSNames = buildEndpointDNSNames(container, endpointConfig.Aliases)
}
if err := validateEndpointSettings(n, n.Name(), endpointConfig); err != nil {
@ -687,6 +666,29 @@ func (daemon *Daemon) updateNetworkConfig(container *container.Container, n *lib
return nil
}
// buildEndpointDNSNames constructs the list of DNSNames that should be assigned to a given endpoint. The order within
// the returned slice is important as the first entry will be used to generate the PTR records (for IPv4 and v6)
// associated to this endpoint.
func buildEndpointDNSNames(ctr *container.Container, aliases []string) []string {
var dnsNames []string
if ctr.Name != "" {
dnsNames = append(dnsNames, strings.TrimPrefix(ctr.Name, "/"))
}
dnsNames = append(dnsNames, aliases...)
if ctr.ID != "" {
dnsNames = append(dnsNames, stringid.TruncateID(ctr.ID))
}
if ctr.Config.Hostname != "" {
dnsNames = append(dnsNames, ctr.Config.Hostname)
}
return sliceutil.Dedup(dnsNames)
}
func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
start := time.Now()
if container.HostConfig.NetworkMode.IsContainer() {

View file

@ -0,0 +1,55 @@
package daemon
import (
"encoding/json"
"testing"
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/container"
"github.com/docker/docker/libnetwork"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestDNSNamesOrder(t *testing.T) {
d := &Daemon{}
ctr := &container.Container{
ID: "35de8003b19e27f636fc6ecbf4d7072558b872a8544f287fd69ad8182ad59023",
Name: "foobar",
Config: &containertypes.Config{
Hostname: "baz",
},
}
nw := buildNetwork(t, map[string]any{
"id": "1234567890",
"name": "testnet",
"networkType": "bridge",
"enableIPv6": false,
})
epSettings := &networktypes.EndpointSettings{
Aliases: []string{"myctr"},
}
if err := d.updateNetworkConfig(ctr, nw, epSettings, false); err != nil {
t.Fatal(err)
}
assert.Check(t, is.DeepEqual(epSettings.DNSNames, []string{"foobar", "myctr", "35de8003b19e", "baz"}))
}
func buildNetwork(t *testing.T, config map[string]any) *libnetwork.Network {
t.Helper()
b, err := json.Marshal(config)
if err != nil {
t.Fatal(err)
}
nw := &libnetwork.Network{}
if err := nw.UnmarshalJSON(b); err != nil {
t.Fatal(err)
}
return nw
}

View file

@ -15,6 +15,8 @@ import (
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/internal/sliceutil"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/go-connections/nat"
)
@ -27,6 +29,18 @@ func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bo
return daemon.containerInspectPre120(ctx, name)
case versions.Equal(version, "1.20"):
return daemon.containerInspect120(name)
case versions.LessThan(version, "1.45"):
ctr, err := daemon.ContainerInspectCurrent(ctx, name, size)
if err != nil {
return nil, err
}
shortCID := stringid.TruncateID(ctr.ID)
for _, ep := range ctr.NetworkSettings.Networks {
ep.Aliases = sliceutil.Dedup(append(ep.Aliases, shortCID, ctr.Config.Hostname))
}
return ctr, nil
default:
return daemon.ContainerInspectCurrent(ctx, name, size)
}

View file

@ -793,10 +793,6 @@ func buildCreateEndpointOptions(c *container.Container, n *libnetwork.Network, e
var genericOptions = make(options.Generic)
nwName := n.Name()
defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
if c.NetworkSettings.IsAnonymousEndpoint || (nwName == defaultNetName && !serviceDiscoveryOnDefaultNetwork()) {
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
}
if epConfig != nil {
if ipam := epConfig.IPAMConfig; ipam != nil {
@ -822,9 +818,8 @@ func buildCreateEndpointOptions(c *container.Container, n *libnetwork.Network, e
createOptions = append(createOptions, libnetwork.CreateOptionIpam(ip, ip6, ipList, nil))
}
for _, alias := range epConfig.Aliases {
createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias))
}
createOptions = append(createOptions, libnetwork.CreateOptionDNSNames(epConfig.DNSNames))
for k, v := range epConfig.DriverOpts {
createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(options.Generic{k: v}))
}

View file

@ -24,7 +24,6 @@ type Settings struct {
Ports nat.PortMap
SecondaryIPAddresses []networktypes.Address
SecondaryIPv6Addresses []networktypes.Address
IsAnonymousEndpoint bool
HasSwarmEndpoint bool
}

View file

@ -7,6 +7,7 @@ import (
"github.com/containerd/log"
"github.com/docker/docker/api/types/events"
dockercontainer "github.com/docker/docker/container"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/libnetwork"
"github.com/pkg/errors"
@ -38,7 +39,6 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) {
defer container.Unlock()
oldName = container.Name
oldIsAnonymousEndpoint := container.NetworkSettings.IsAnonymousEndpoint
if oldName == newName {
return errdefs.InvalidParameter(errors.New("Renaming a container with the same name as its current name"))
@ -62,12 +62,10 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) {
}
container.Name = newName
container.NetworkSettings.IsAnonymousEndpoint = false
defer func() {
if retErr != nil {
container.Name = oldName
container.NetworkSettings.IsAnonymousEndpoint = oldIsAnonymousEndpoint
daemon.reserveName(container.ID, oldName)
for k, v := range links {
daemon.containersReplica.ReserveName(oldName+k, v.ID)
@ -101,7 +99,6 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) {
defer func() {
if retErr != nil {
container.Name = oldName
container.NetworkSettings.IsAnonymousEndpoint = oldIsAnonymousEndpoint
if err := container.CheckpointTo(daemon.containersReplica); err != nil {
log.G(context.TODO()).WithFields(log.Fields{
"containerID": container.ID,
@ -118,10 +115,57 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) (retErr error) {
return err
}
err = sb.Rename(strings.TrimPrefix(container.Name, "/"))
if err != nil {
if err := sb.Rename(newName[1:]); err != nil {
return err
}
defer func() {
if retErr != nil {
if err := sb.Rename(oldName); err != nil {
log.G(context.TODO()).WithFields(log.Fields{
"sandboxID": sid,
"oldName": oldName,
"newName": newName,
"error": err,
}).Errorf("failed to revert sandbox rename")
}
}
}()
for nwName, epConfig := range container.NetworkSettings.Networks {
nw, err := daemon.FindNetwork(nwName)
if err != nil {
return err
}
ep, err := nw.EndpointByID(epConfig.EndpointID)
if err != nil {
return err
}
oldDNSNames := make([]string, len(epConfig.DNSNames))
copy(oldDNSNames, epConfig.DNSNames)
epConfig.DNSNames = buildEndpointDNSNames(container, epConfig.Aliases)
if err := ep.UpdateDNSNames(epConfig.DNSNames); err != nil {
return err
}
defer func(ep *libnetwork.Endpoint, epConfig *network.EndpointSettings, oldDNSNames []string) {
if retErr == nil {
return
}
epConfig.DNSNames = oldDNSNames
if err := ep.UpdateDNSNames(epConfig.DNSNames); err != nil {
log.G(context.TODO()).WithFields(log.Fields{
"sandboxID": sid,
"oldName": oldName,
"newName": newName,
"error": err,
}).Errorf("failed to revert DNSNames update")
}
}(ep, epConfig, oldDNSNames)
}
}
daemon.LogContainerEventWithAttributes(container, events.ActionRename, attributes)

View file

@ -68,6 +68,13 @@ keywords: "API, Docker, rcli, REST, documentation"
* The `Container` and `ContainerConfig` fields in the `GET /images/{name}/json`
response are deprecated and will no longer be included in API v1.45.
* `GET /info` now includes `status` properties in `Runtimes`.
* A new field named `DNSNames` and containing all non-fully qualified DNS names
a container takes on a specific network has been added to `GET /containers/{name:.*}/json`.
* The `Aliases` field returned in calls to `GET /containers/{name:.*}/json` in v1.44 and older
versions contains the short container ID. This will change in the next API version, v1.45.
Starting with that API version, this specific value will be removed from the `Aliases` field
such that this field will reflect exactly the values originally submitted to the
`POST /containers/create` endpoint. The newly introduced `DNSNames` should now be used instead.
## v1.43 API changes

View file

@ -13,7 +13,8 @@ source hack/make/.integration-test-helpers
# --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream
# TODO re-enable test_attach_no_stream after https://github.com/docker/docker-py/issues/2513 is resolved
# TODO re-enable test_run_container_reading_socket_ws. It's reported in https://github.com/docker/docker-py/issues/1478, and we're getting that error in our tests.
: "${PY_TEST_OPTIONS:=--junitxml=${DEST}/junit-report.xml --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_run_container_reading_socket_ws}"
# TODO re-enable test_run_with_networking_config once this issue is fixed: https://github.com/moby/moby/pull/46853#issuecomment-1864679942.
: "${PY_TEST_OPTIONS:=--junitxml=${DEST}/junit-report.xml --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_run_container_reading_socket_ws --deselect=tests/integration/models_containers_test.py::ContainerCollectionTest::test_run_with_networking_config}"
# build --squash is not supported with containerd integration.
if [ -n "$TEST_INTEGRATION_USE_SNAPSHOTTER" ]; then

View file

@ -5,6 +5,7 @@ import (
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/container"
net "github.com/docker/docker/integration/internal/network"
"github.com/docker/docker/integration/internal/swarm"
@ -13,13 +14,13 @@ import (
"gotest.tools/v3/skip"
)
func TestDockerNetworkConnectAlias(t *testing.T) {
func TestDockerNetworkConnectAliasPreV144(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
ctx := setupTest(t)
d := swarm.NewSwarm(ctx, t, testEnv)
defer d.Stop(t)
client := d.NewClientT(t)
client := d.NewClientT(t, client.WithVersion("1.43"))
defer client.Close()
name := t.Name() + "test-alias"

View file

@ -0,0 +1,13 @@
package sliceutil
func Dedup[T comparable](slice []T) []T {
keys := make(map[T]struct{})
out := make([]T, 0, len(slice))
for _, s := range slice {
if _, ok := keys[s]; !ok {
out = append(out, s)
keys[s] = struct{}{}
}
}
return out
}

View file

@ -598,7 +598,7 @@ func (ep *Endpoint) deleteDriverInfoFromCluster() error {
}
func (ep *Endpoint) addServiceInfoToCluster(sb *Sandbox) error {
if len(ep.myAliases) == 0 && ep.isAnonymous() || ep.Iface() == nil || ep.Iface().Address() == nil {
if len(ep.dnsNames) == 0 || ep.Iface() == nil || ep.Iface().Address() == nil {
return nil
}
@ -628,10 +628,8 @@ func (ep *Endpoint) addServiceInfoToCluster(sb *Sandbox) error {
return nil
}
name := ep.Name()
if ep.isAnonymous() {
name = ep.MyAliases()[0]
}
dnsNames := ep.getDNSNames()
primaryDNSName, dnsAliases := dnsNames[0], dnsNames[1:]
var ingressPorts []*PortConfig
if ep.svcID != "" {
@ -640,24 +638,24 @@ func (ep *Endpoint) addServiceInfoToCluster(sb *Sandbox) error {
if n.ingress {
ingressPorts = ep.ingressPorts
}
if err := n.getController().addServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), name, ep.virtualIP, ingressPorts, ep.svcAliases, ep.myAliases, ep.Iface().Address().IP, "addServiceInfoToCluster"); err != nil {
if err := n.getController().addServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), primaryDNSName, ep.virtualIP, ingressPorts, ep.svcAliases, dnsAliases, ep.Iface().Address().IP, "addServiceInfoToCluster"); err != nil {
return err
}
} else {
// This is a container simply attached to an attachable network
if err := n.getController().addContainerNameResolution(n.ID(), ep.ID(), name, ep.myAliases, ep.Iface().Address().IP, "addServiceInfoToCluster"); err != nil {
if err := n.getController().addContainerNameResolution(n.ID(), ep.ID(), primaryDNSName, dnsAliases, ep.Iface().Address().IP, "addServiceInfoToCluster"); err != nil {
return err
}
}
buf, err := proto.Marshal(&EndpointRecord{
Name: name,
Name: primaryDNSName,
ServiceName: ep.svcName,
ServiceID: ep.svcID,
VirtualIP: ep.virtualIP.String(),
IngressPorts: ingressPorts,
Aliases: ep.svcAliases,
TaskAliases: ep.myAliases,
TaskAliases: dnsAliases,
EndpointIP: ep.Iface().Address().IP.String(),
ServiceDisabled: false,
})
@ -676,7 +674,7 @@ func (ep *Endpoint) addServiceInfoToCluster(sb *Sandbox) error {
}
func (ep *Endpoint) deleteServiceInfoFromCluster(sb *Sandbox, fullRemove bool, method string) error {
if len(ep.myAliases) == 0 && ep.isAnonymous() {
if len(ep.dnsNames) == 0 {
return nil
}
@ -699,10 +697,8 @@ func (ep *Endpoint) deleteServiceInfoFromCluster(sb *Sandbox, fullRemove bool, m
return nil
}
name := ep.Name()
if ep.isAnonymous() {
name = ep.MyAliases()[0]
}
dnsNames := ep.getDNSNames()
primaryDNSName, dnsAliases := dnsNames[0], dnsNames[1:]
// First update the networkDB then locally
if fullRemove {
@ -720,12 +716,12 @@ func (ep *Endpoint) deleteServiceInfoFromCluster(sb *Sandbox, fullRemove bool, m
if n.ingress {
ingressPorts = ep.ingressPorts
}
if err := n.getController().rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), name, ep.virtualIP, ingressPorts, ep.svcAliases, ep.myAliases, ep.Iface().Address().IP, "deleteServiceInfoFromCluster", true, fullRemove); err != nil {
if err := n.getController().rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), primaryDNSName, ep.virtualIP, ingressPorts, ep.svcAliases, dnsAliases, ep.Iface().Address().IP, "deleteServiceInfoFromCluster", true, fullRemove); err != nil {
return err
}
} else {
// This is a container simply attached to an attachable network
if err := n.getController().delContainerNameResolution(n.ID(), ep.ID(), name, ep.myAliases, ep.Iface().Address().IP, "deleteServiceInfoFromCluster"); err != nil {
if err := n.getController().delContainerNameResolution(n.ID(), ep.ID(), primaryDNSName, dnsAliases, ep.Iface().Address().IP, "deleteServiceInfoFromCluster"); err != nil {
return err
}
}

View file

@ -47,7 +47,7 @@ func (sb *Sandbox) setupDefaultGW() error {
}
}
createOptions := []EndpointOption{CreateOptionAnonymous()}
createOptions := []EndpointOption{}
var gwName string
if len(sb.containerID) <= gwEPlen {

View file

@ -8,6 +8,7 @@ import (
"sync"
"github.com/containerd/log"
"github.com/docker/docker/internal/sliceutil"
"github.com/docker/docker/libnetwork/datastore"
"github.com/docker/docker/libnetwork/ipamapi"
"github.com/docker/docker/libnetwork/netlabel"
@ -23,21 +24,22 @@ type EndpointOption func(ep *Endpoint)
// Endpoint represents a logical connection between a network and a sandbox.
type Endpoint struct {
name string
id string
network *Network
iface *EndpointInterface
joinInfo *endpointJoinInfo
sandboxID string
exposedPorts []types.TransportPort
anonymous bool
name string
id string
network *Network
iface *EndpointInterface
joinInfo *endpointJoinInfo
sandboxID string
exposedPorts []types.TransportPort
// dnsNames holds all the non-fully qualified DNS names associated to this endpoint. Order matters: first entry
// will be used for the PTR records associated to the endpoint's IPv4 and IPv6 addresses.
dnsNames []string
disableResolution bool
generic map[string]interface{}
prefAddress net.IP
prefAddressV6 net.IP
ipamOptions map[string]string
aliases map[string]string
myAliases []string
svcID string
svcName string
virtualIP net.IP
@ -64,9 +66,8 @@ func (ep *Endpoint) MarshalJSON() ([]byte, error) {
epMap["generic"] = ep.generic
}
epMap["sandbox"] = ep.sandboxID
epMap["anonymous"] = ep.anonymous
epMap["dnsNames"] = ep.dnsNames
epMap["disableResolution"] = ep.disableResolution
epMap["myAliases"] = ep.myAliases
epMap["svcName"] = ep.svcName
epMap["svcID"] = ep.svcID
epMap["virtualIP"] = ep.virtualIP.String()
@ -156,8 +157,9 @@ func (ep *Endpoint) UnmarshalJSON(b []byte) (err error) {
}
}
var anonymous bool
if v, ok := epMap["anonymous"]; ok {
ep.anonymous = v.(bool)
anonymous = v.(bool)
}
if v, ok := epMap["disableResolution"]; ok {
ep.disableResolution = v.(bool)
@ -192,7 +194,23 @@ func (ep *Endpoint) UnmarshalJSON(b []byte) (err error) {
ma, _ := json.Marshal(epMap["myAliases"])
var myAliases []string
json.Unmarshal(ma, &myAliases) //nolint:errcheck
ep.myAliases = myAliases
_, hasDNSNames := epMap["dnsNames"]
dn, _ := json.Marshal(epMap["dnsNames"])
var dnsNames []string
json.Unmarshal(dn, &dnsNames)
ep.dnsNames = dnsNames
// TODO(aker): remove this migration code in v27
if !hasDNSNames {
// The field dnsNames was introduced in v25.0. If we don't have it, the on-disk state was written by an older
// daemon, thus we need to populate dnsNames based off of myAliases and anonymous values.
if !anonymous {
myAliases = append([]string{ep.name}, myAliases...)
}
ep.dnsNames = sliceutil.Dedup(myAliases)
}
return nil
}
@ -210,7 +228,6 @@ func (ep *Endpoint) CopyTo(o datastore.KVObject) error {
dstEp.sandboxID = ep.sandboxID
dstEp.dbIndex = ep.dbIndex
dstEp.dbExists = ep.dbExists
dstEp.anonymous = ep.anonymous
dstEp.disableResolution = ep.disableResolution
dstEp.svcName = ep.svcName
dstEp.svcID = ep.svcID
@ -240,8 +257,8 @@ func (ep *Endpoint) CopyTo(o datastore.KVObject) error {
dstEp.exposedPorts = make([]types.TransportPort, len(ep.exposedPorts))
copy(dstEp.exposedPorts, ep.exposedPorts)
dstEp.myAliases = make([]string, len(ep.myAliases))
copy(dstEp.myAliases, ep.myAliases)
dstEp.dnsNames = make([]string, len(ep.dnsNames))
copy(dstEp.dnsNames, ep.dnsNames)
dstEp.generic = options.Generic{}
for k, v := range ep.generic {
@ -267,13 +284,6 @@ func (ep *Endpoint) Name() string {
return ep.name
}
func (ep *Endpoint) MyAliases() []string {
ep.mu.Lock()
defer ep.mu.Unlock()
return ep.myAliases
}
// Network returns the name of the network to which this endpoint is attached.
func (ep *Endpoint) Network() string {
if ep.network == nil {
@ -283,10 +293,15 @@ func (ep *Endpoint) Network() string {
return ep.network.name
}
func (ep *Endpoint) isAnonymous() bool {
// getDNSNames returns a copy of the DNS names associated to this endpoint. The first entry is the one used for PTR
// records.
func (ep *Endpoint) getDNSNames() []string {
ep.mu.Lock()
defer ep.mu.Unlock()
return ep.anonymous
dnsNames := make([]string, len(ep.dnsNames))
copy(dnsNames, ep.dnsNames)
return dnsNames
}
// isServiceEnabled check if service is enabled on the endpoint
@ -568,71 +583,52 @@ func (ep *Endpoint) sbJoin(sb *Sandbox, options ...EndpointOption) (err error) {
}
func (ep *Endpoint) rename(name string) error {
var (
err error
ok bool
)
ep.mu.Lock()
ep.name = name
ep.mu.Unlock()
n := ep.getNetwork()
if n == nil {
return fmt.Errorf("network not connected for ep %q", ep.name)
// Update the store with the updated name
if err := ep.getNetwork().getController().updateToStore(ep); err != nil {
return err
}
c := n.getController()
return nil
}
func (ep *Endpoint) UpdateDNSNames(dnsNames []string) error {
nw := ep.getNetwork()
c := nw.getController()
sb, ok := ep.getSandbox()
if !ok {
log.G(context.TODO()).Warnf("rename for %s aborted, sandbox %s is not anymore present", ep.ID(), ep.sandboxID)
log.G(context.TODO()).WithFields(log.Fields{
"sandboxID": ep.sandboxID,
"endpointID": ep.ID(),
}).Warn("DNSNames update aborted, sandbox is not present anymore")
return nil
}
if c.isAgent() {
if err = ep.deleteServiceInfoFromCluster(sb, true, "rename"); err != nil {
return types.InternalErrorf("Could not delete service state for endpoint %s from cluster on rename: %v", ep.Name(), err)
if err := ep.deleteServiceInfoFromCluster(sb, true, "UpdateDNSNames"); err != nil {
return types.InternalErrorf("could not delete service state for endpoint %s from cluster on UpdateDNSNames: %v", ep.Name(), err)
}
ep.dnsNames = dnsNames
if err := ep.addServiceInfoToCluster(sb); err != nil {
return types.InternalErrorf("could not add service state for endpoint %s to cluster on UpdateDNSNames: %v", ep.Name(), err)
}
} else {
n.updateSvcRecord(ep, false)
}
nw.updateSvcRecord(ep, false)
oldName := ep.name
oldAnonymous := ep.anonymous
ep.name = name
ep.anonymous = false
if c.isAgent() {
if err = ep.addServiceInfoToCluster(sb); err != nil {
return types.InternalErrorf("Could not add service state for endpoint %s to cluster on rename: %v", ep.Name(), err)
}
defer func() {
if err != nil {
if err2 := ep.deleteServiceInfoFromCluster(sb, true, "rename"); err2 != nil {
log.G(context.TODO()).WithField("main error", err).WithError(err2).Debug("Error during cleanup due deleting service info from cluster while cleaning up due to other error")
}
ep.name = oldName
ep.anonymous = oldAnonymous
if err2 := ep.addServiceInfoToCluster(sb); err2 != nil {
log.G(context.TODO()).WithField("main error", err).WithError(err2).Debug("Error during cleanup due adding service to from cluster while cleaning up due to other error")
}
}
}()
} else {
n.updateSvcRecord(ep, true)
defer func() {
if err != nil {
n.updateSvcRecord(ep, false)
ep.name = oldName
ep.anonymous = oldAnonymous
n.updateSvcRecord(ep, true)
}
}()
ep.dnsNames = dnsNames
nw.updateSvcRecord(ep, true)
}
// Update the store with the updated name
if err = c.updateToStore(ep); err != nil {
if err := c.updateToStore(ep); err != nil {
return err
}
return err
return nil
}
func (ep *Endpoint) hasInterface(iName string) bool {
@ -951,11 +947,11 @@ func CreateOptionDNS(dns []string) EndpointOption {
}
}
// CreateOptionAnonymous function returns an option setter for setting
// this endpoint as anonymous
func CreateOptionAnonymous() EndpointOption {
// CreateOptionDNSNames specifies the list of (non fully qualified) DNS names associated to an endpoint. These will be
// used to populate the embedded DNS server. Order matters: first name will be used to generate PTR records.
func CreateOptionDNSNames(names []string) EndpointOption {
return func(ep *Endpoint) {
ep.anonymous = true
ep.dnsNames = names
}
}
@ -988,13 +984,6 @@ func CreateOptionService(name, id string, vip net.IP, ingressPorts []*PortConfig
}
}
// CreateOptionMyAlias function returns an option setter for setting endpoint's self alias
func CreateOptionMyAlias(alias string) EndpointOption {
return func(ep *Endpoint) {
ep.myAliases = append(ep.myAliases, alias)
}
}
// CreateOptionLoadBalancer function returns an option setter for denoting the endpoint is a load balancer for a network
func CreateOptionLoadBalancer() EndpointOption {
return func(ep *Endpoint) {

View file

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net"
"reflect"
"runtime"
"testing"
"time"
@ -191,7 +192,6 @@ func TestEndpointMarshalling(t *testing.T) {
name: "Bau",
id: "efghijklmno",
sandboxID: "ambarabaciccicocco",
anonymous: true,
iface: &EndpointInterface{
mac: []byte{11, 12, 13, 14, 15, 16},
addr: &net.IPNet{
@ -205,6 +205,7 @@ func TestEndpointMarshalling(t *testing.T) {
v6PoolID: "poolv6",
llAddrs: lla,
},
dnsNames: []string{"test", "foobar", "baz"},
}
b, err := json.Marshal(e)
@ -218,7 +219,7 @@ func TestEndpointMarshalling(t *testing.T) {
t.Fatal(err)
}
if e.name != ee.name || e.id != ee.id || e.sandboxID != ee.sandboxID || !compareEndpointInterface(e.iface, ee.iface) || e.anonymous != ee.anonymous {
if e.name != ee.name || e.id != ee.id || e.sandboxID != ee.sandboxID || !reflect.DeepEqual(e.dnsNames, ee.dnsNames) || !compareEndpointInterface(e.iface, ee.iface) {
t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%#v\nDecoded:\n%#v\nOriginal iface: %#v\nDecodediface:\n%#v", e, ee, e.iface, ee.iface)
}
}

View file

@ -175,7 +175,7 @@ func (i *IpamInfo) UnmarshalJSON(data []byte) error {
type Network struct {
ctrlr *Controller
name string
networkType string
networkType string // networkType is the name of the netdriver used by this network
id string
created time.Time
scope string // network data scope
@ -1302,8 +1302,6 @@ func (n *Network) updateSvcRecord(ep *Endpoint, isAdd bool) {
}
var ipv6 net.IP
epName := ep.Name()
myAliases := ep.MyAliases()
if iface.AddressIPv6() != nil {
ipv6 = iface.AddressIPv6().IP
}
@ -1312,30 +1310,17 @@ func (n *Network) updateSvcRecord(ep *Endpoint, isAdd bool) {
if serviceID == "" {
serviceID = ep.ID()
}
dnsNames := ep.getDNSNames()
if isAdd {
// If anonymous endpoint has an alias use the first alias
// for ip->name mapping. Not having the reverse mapping
// breaks some apps
if ep.isAnonymous() {
if len(myAliases) > 0 {
n.addSvcRecords(ep.ID(), myAliases[0], serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
}
} else {
n.addSvcRecords(ep.ID(), epName, serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
}
for _, alias := range myAliases {
n.addSvcRecords(ep.ID(), alias, serviceID, iface.Address().IP, ipv6, false, "updateSvcRecord")
for i, dnsName := range dnsNames {
ipMapUpdate := i == 0 // ipMapUpdate indicates whether PTR records should be updated.
n.addSvcRecords(ep.ID(), dnsName, serviceID, iface.Address().IP, ipv6, ipMapUpdate, "updateSvcRecord")
}
} else {
if ep.isAnonymous() {
if len(myAliases) > 0 {
n.deleteSvcRecords(ep.ID(), myAliases[0], serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
}
} else {
n.deleteSvcRecords(ep.ID(), epName, serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
}
for _, alias := range myAliases {
n.deleteSvcRecords(ep.ID(), alias, serviceID, iface.Address().IP, ipv6, false, "updateSvcRecord")
for i, dnsName := range dnsNames {
ipMapUpdate := i == 0 // ipMapUpdate indicates whether PTR records should be updated.
n.deleteSvcRecords(ep.ID(), dnsName, serviceID, iface.Address().IP, ipv6, ipMapUpdate, "updateSvcRecord")
}
}
}
@ -1374,6 +1359,7 @@ func delNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID strin
})
}
// TODO(aker): remove ipMapUpdate param and add a proper method dedicated to update PTR records.
func (n *Network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP, ipMapUpdate bool, method string) {
// Do not add service names for ingress network as this is a
// routing only network
@ -2176,10 +2162,6 @@ func (n *Network) createLoadBalancerSandbox() (retErr error) {
CreateOptionIpam(n.loadBalancerIP, nil, nil, nil),
CreateOptionLoadBalancer(),
}
if n.hasLoadBalancerEndpoint() && !n.ingress {
// Mark LB endpoints as anonymous so they don't show up in DNS
epOptions = append(epOptions, CreateOptionAnonymous())
}
ep, err := n.createEndpoint(endpointName, epOptions...)
if err != nil {
return err