mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Merge pull request #49300 from thaJeztah/links_fixes
daemon/links: assorted bug fixes and cleanup
This commit is contained in:
@@ -30,15 +30,13 @@ import (
|
||||
)
|
||||
|
||||
func (daemon *Daemon) setupLinkedContainers(ctr *container.Container) ([]string, error) {
|
||||
var env []string
|
||||
children := daemon.children(ctr)
|
||||
|
||||
bridgeSettings := ctr.NetworkSettings.Networks[network.DefaultNetwork]
|
||||
if bridgeSettings == nil || bridgeSettings.EndpointSettings == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for linkAlias, child := range children {
|
||||
var env []string
|
||||
for linkAlias, child := range daemon.children(ctr) {
|
||||
if !child.IsRunning() {
|
||||
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
|
||||
}
|
||||
@@ -48,7 +46,7 @@ func (daemon *Daemon) setupLinkedContainers(ctr *container.Container) ([]string,
|
||||
return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID)
|
||||
}
|
||||
|
||||
link := links.NewLink(
|
||||
linkEnvVars := links.EnvVars(
|
||||
bridgeSettings.IPAddress,
|
||||
childBridgeSettings.IPAddress,
|
||||
linkAlias,
|
||||
@@ -56,7 +54,7 @@ func (daemon *Daemon) setupLinkedContainers(ctr *container.Container) ([]string,
|
||||
child.Config.ExposedPorts,
|
||||
)
|
||||
|
||||
env = append(env, link.ToEnv()...)
|
||||
env = append(env, linkEnvVars...)
|
||||
}
|
||||
|
||||
return env, nil
|
||||
|
||||
@@ -22,16 +22,17 @@ type Link struct {
|
||||
Ports []nat.Port
|
||||
}
|
||||
|
||||
// EnvVars generates environment variables for the linked container
|
||||
// for the Link with the given options.
|
||||
func EnvVars(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}) []string {
|
||||
return NewLink(parentIP, childIP, name, env, exposedPorts).ToEnv()
|
||||
}
|
||||
|
||||
// NewLink initializes a new Link struct with the provided options.
|
||||
func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}) *Link {
|
||||
var (
|
||||
i int
|
||||
ports = make([]nat.Port, len(exposedPorts))
|
||||
)
|
||||
|
||||
ports := make([]nat.Port, 0, len(exposedPorts))
|
||||
for p := range exposedPorts {
|
||||
ports[i] = p
|
||||
i++
|
||||
ports = append(ports, p)
|
||||
}
|
||||
|
||||
return &Link{
|
||||
@@ -47,46 +48,47 @@ func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.
|
||||
// the form of environment variables which will be later exported on container
|
||||
// startup.
|
||||
func (l *Link) ToEnv() []string {
|
||||
env := []string{}
|
||||
|
||||
_, n := path.Split(l.Name)
|
||||
alias := strings.ReplaceAll(strings.ToUpper(n), "-", "_")
|
||||
|
||||
if p := l.getDefaultPort(); p != nil {
|
||||
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
|
||||
}
|
||||
|
||||
// sort the ports so that we can bulk the continuous ports together
|
||||
nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
|
||||
// If the two ports have the same number, tcp takes priority
|
||||
// Sort in desc order
|
||||
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
|
||||
})
|
||||
nat.Sort(l.Ports, withTCPPriority)
|
||||
|
||||
for i := 0; i < len(l.Ports); {
|
||||
p := l.Ports[i]
|
||||
j := nextContiguous(l.Ports, p.Int(), i)
|
||||
if j > i+1 {
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_START=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
|
||||
|
||||
q := l.Ports[j]
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_END=%s://%s:%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Proto(), l.ChildIP, q.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port()))
|
||||
|
||||
i = j + 1
|
||||
continue
|
||||
} else {
|
||||
i++
|
||||
var pStart, pEnd nat.Port
|
||||
env := make([]string, 0, 1+len(l.Ports)*4)
|
||||
for i, p := range l.Ports {
|
||||
if i == 0 {
|
||||
pStart, pEnd = p, p
|
||||
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
|
||||
}
|
||||
}
|
||||
for _, p := range l.Ports {
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
|
||||
|
||||
// These env-vars are produced for every port, regardless if they're
|
||||
// part of a port-range.
|
||||
prefix := fmt.Sprintf("%s_PORT_%s_%s", alias, p.Port(), strings.ToUpper(p.Proto()))
|
||||
env = append(env, fmt.Sprintf("%s=%s://%s:%s", prefix, p.Proto(), l.ChildIP, p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_ADDR=%s", prefix, l.ChildIP))
|
||||
env = append(env, fmt.Sprintf("%s_PORT=%s", prefix, p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PROTO=%s", prefix, p.Proto()))
|
||||
|
||||
// Detect whether this port is part of a range (consecutive port number
|
||||
// and same protocol).
|
||||
if p.Int() == pEnd.Int()+1 && strings.EqualFold(p.Proto(), pStart.Proto()) {
|
||||
pEnd = p
|
||||
if i < len(l.Ports)-1 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if pEnd != pStart {
|
||||
prefix = fmt.Sprintf("%s_PORT_%s_%s", alias, pStart.Port(), strings.ToUpper(pStart.Proto()))
|
||||
env = append(env, fmt.Sprintf("%s_START=%s://%s:%s", prefix, pStart.Proto(), l.ChildIP, pStart.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_START=%s", prefix, pStart.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_END=%s://%s:%s", prefix, pEnd.Proto(), l.ChildIP, pEnd.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_END=%s", prefix, pEnd.Port()))
|
||||
}
|
||||
|
||||
// Reset for next range (if any)
|
||||
pStart, pEnd = p, p
|
||||
}
|
||||
|
||||
// Load the linked container's name into the environment
|
||||
@@ -108,34 +110,19 @@ func (l *Link) ToEnv() []string {
|
||||
return env
|
||||
}
|
||||
|
||||
func nextContiguous(ports []nat.Port, value int, index int) int {
|
||||
if index+1 == len(ports) {
|
||||
return index
|
||||
// withTCPPriority prioritizes ports using TCP over other protocols before
|
||||
// comparing port-number and protocol.
|
||||
func withTCPPriority(ip, jp nat.Port) bool {
|
||||
if strings.EqualFold(ip.Proto(), jp.Proto()) {
|
||||
return ip.Int() < jp.Int()
|
||||
}
|
||||
for i := index + 1; i < len(ports); i++ {
|
||||
if ports[i].Int() > value+1 {
|
||||
return i - 1
|
||||
}
|
||||
|
||||
value++
|
||||
if strings.EqualFold(ip.Proto(), "tcp") {
|
||||
return true
|
||||
}
|
||||
return len(ports) - 1
|
||||
}
|
||||
|
||||
// Default port rules
|
||||
func (l *Link) getDefaultPort() *nat.Port {
|
||||
var p nat.Port
|
||||
i := len(l.Ports)
|
||||
|
||||
if i == 0 {
|
||||
return nil
|
||||
} else if i > 1 {
|
||||
nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
|
||||
// If the two ports have the same number, tcp takes priority
|
||||
// Sort in desc order
|
||||
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
|
||||
})
|
||||
}
|
||||
p = l.Ports[0]
|
||||
return &p
|
||||
if strings.EqualFold(jp.Proto(), "tcp") {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.ToLower(ip.Proto()) < strings.ToLower(jp.Proto())
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestLinkNaming(t *testing.T) {
|
||||
link := NewLink("172.0.17.3", "172.0.17.2", "/db/docker-1", nil, nat.PortSet{
|
||||
actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker-1", nil, nat.PortSet{
|
||||
"6379/tcp": struct{}{},
|
||||
})
|
||||
|
||||
@@ -22,7 +22,6 @@ func TestLinkNaming(t *testing.T) {
|
||||
"DOCKER_1_PORT_6379_TCP_PROTO=tcp",
|
||||
}
|
||||
|
||||
actual := link.ToEnv()
|
||||
sort.Strings(actual) // order of env-vars is not relevant
|
||||
assert.DeepEqual(t, expectedEnv, actual)
|
||||
}
|
||||
@@ -43,7 +42,7 @@ func TestLinkNew(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLinkEnv(t *testing.T) {
|
||||
link := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, nat.PortSet{
|
||||
actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, nat.PortSet{
|
||||
"6379/tcp": struct{}{},
|
||||
})
|
||||
|
||||
@@ -57,31 +56,65 @@ func TestLinkEnv(t *testing.T) {
|
||||
"DOCKER_PORT_6379_TCP_PROTO=tcp",
|
||||
}
|
||||
|
||||
actual := link.ToEnv()
|
||||
sort.Strings(actual) // order of env-vars is not relevant
|
||||
assert.DeepEqual(t, expectedEnv, actual)
|
||||
}
|
||||
|
||||
// TestSortPorts verifies that ports are sorted with TCP taking priority,
|
||||
// and ports with the same protocol to be sorted by port.
|
||||
func TestSortPorts(t *testing.T) {
|
||||
ports := []nat.Port{
|
||||
"6379/tcp",
|
||||
"6376/udp",
|
||||
"6380/tcp",
|
||||
"6376/sctp",
|
||||
"6381/tcp",
|
||||
"6381/udp",
|
||||
"6375/udp",
|
||||
"6375/sctp",
|
||||
}
|
||||
|
||||
expected := []nat.Port{
|
||||
"6379/tcp",
|
||||
"6380/tcp",
|
||||
"6381/tcp",
|
||||
"6375/sctp",
|
||||
"6376/sctp",
|
||||
"6375/udp",
|
||||
"6376/udp",
|
||||
"6381/udp",
|
||||
}
|
||||
|
||||
nat.Sort(ports, withTCPPriority)
|
||||
assert.DeepEqual(t, expected, ports)
|
||||
}
|
||||
|
||||
func TestLinkMultipleEnv(t *testing.T) {
|
||||
link := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, nat.PortSet{
|
||||
actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, nat.PortSet{
|
||||
"6300/udp": struct{}{},
|
||||
"6379/tcp": struct{}{},
|
||||
"6380/tcp": struct{}{},
|
||||
"6381/tcp": struct{}{},
|
||||
"6382/udp": struct{}{},
|
||||
})
|
||||
|
||||
expectedEnv := []string{
|
||||
"DOCKER_ENV_PASSWORD=gordon",
|
||||
"DOCKER_NAME=/db/docker",
|
||||
"DOCKER_PORT=tcp://172.0.17.2:6379",
|
||||
|
||||
"DOCKER_PORT_6300_UDP=udp://172.0.17.2:6300",
|
||||
"DOCKER_PORT_6300_UDP_ADDR=172.0.17.2",
|
||||
"DOCKER_PORT_6300_UDP_PORT=6300",
|
||||
"DOCKER_PORT_6300_UDP_PROTO=udp",
|
||||
|
||||
"DOCKER_PORT_6379_TCP=tcp://172.0.17.2:6379",
|
||||
"DOCKER_PORT_6379_TCP_ADDR=172.0.17.2",
|
||||
"DOCKER_PORT_6379_TCP_ADDR=172.0.17.2", // FIXME(thaJeztah): duplicate?
|
||||
"DOCKER_PORT_6379_TCP_END=tcp://172.0.17.2:6381",
|
||||
"DOCKER_PORT_6379_TCP_PORT=6379",
|
||||
"DOCKER_PORT_6379_TCP_PORT_END=6381",
|
||||
"DOCKER_PORT_6379_TCP_PORT_START=6379",
|
||||
"DOCKER_PORT_6379_TCP_PROTO=tcp",
|
||||
"DOCKER_PORT_6379_TCP_PROTO=tcp", // FIXME(thaJeztah): duplicate?
|
||||
"DOCKER_PORT_6379_TCP_START=tcp://172.0.17.2:6379",
|
||||
|
||||
"DOCKER_PORT_6380_TCP=tcp://172.0.17.2:6380",
|
||||
@@ -93,9 +126,27 @@ func TestLinkMultipleEnv(t *testing.T) {
|
||||
"DOCKER_PORT_6381_TCP_ADDR=172.0.17.2",
|
||||
"DOCKER_PORT_6381_TCP_PORT=6381",
|
||||
"DOCKER_PORT_6381_TCP_PROTO=tcp",
|
||||
|
||||
"DOCKER_PORT_6382_UDP=udp://172.0.17.2:6382",
|
||||
"DOCKER_PORT_6382_UDP_ADDR=172.0.17.2",
|
||||
"DOCKER_PORT_6382_UDP_PORT=6382",
|
||||
"DOCKER_PORT_6382_UDP_PROTO=udp",
|
||||
}
|
||||
|
||||
actual := link.ToEnv()
|
||||
sort.Strings(actual) // order of env-vars is not relevant
|
||||
assert.DeepEqual(t, expectedEnv, actual)
|
||||
}
|
||||
|
||||
func BenchmarkLinkMultipleEnv(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, nat.PortSet{
|
||||
"6300/udp": struct{}{},
|
||||
"6379/tcp": struct{}{},
|
||||
"6380/tcp": struct{}{},
|
||||
"6381/tcp": struct{}{},
|
||||
"6382/udp": struct{}{},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user