Browse code

daemon: deprecate env vars set by legacy links

The environment variables set by legacy links are not particularly
useful because you need to know the name of the linked container to use
them, or you need to scan all enviornment variables to find them.

Legacy links are deprecated / marked "legacy" since a long time, and we
want to replace them with non-legacy links. This will help make the
default bridge work like custom networks.

For now, stop setting these environment variables inside of linking
containers by default, but provide an escape hatch to allow users who
still rely on these to re-enable them.

The integration-cli tests `TestExecEnvLinksHost` and `TestLinksEnvs` are
removed as they need to run against a daemon with legacy links env vars
enabled, and a new integration test`TestLegacyLinksEnvVars` is added to
fill the gap. Similarly, the docker-py test `test_create_with_links` is
skipped.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>

Albin Kerouanton authored on 2025/08/14 01:04:03
Showing 5 changed files
... ...
@@ -46,15 +46,18 @@ func (daemon *Daemon) setupLinkedContainers(ctr *container.Container) ([]string,
46 46
 			return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID)
47 47
 		}
48 48
 
49
-		linkEnvVars := links.EnvVars(
50
-			bridgeSettings.IPAddress,
51
-			childBridgeSettings.IPAddress,
52
-			linkAlias,
53
-			child.Config.Env,
54
-			child.Config.ExposedPorts,
55
-		)
56
-
57
-		env = append(env, linkEnvVars...)
49
+		// Environment variables defined when using legacy links are deprecated and will be removed in a future release.
50
+		// Allow users to restore the old behavior through this escape hatch.
51
+		if os.Getenv("DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS") == "1" {
52
+			linkEnvVars := links.EnvVars(
53
+				bridgeSettings.IPAddress,
54
+				childBridgeSettings.IPAddress,
55
+				linkAlias,
56
+				child.Config.Env,
57
+				child.Config.ExposedPorts,
58
+			)
59
+			env = append(env, linkEnvVars...)
60
+		}
58 61
 	}
59 62
 
60 63
 	return env, nil
... ...
@@ -37,6 +37,11 @@ PY_TEST_OPTIONS="$PY_TEST_OPTIONS --deselect=tests/integration/api_image_test.py
37 37
 
38 38
 # TODO(laurazard): re-enable after https://github.com/docker/docker-py/pull/3290 is included in the DOCKER_PY_COMMIT release.
39 39
 PY_TEST_OPTIONS="$PY_TEST_OPTIONS --deselect=tests/integration/models_containers_test.py::ContainerTest::test_exec_run_failed"
40
+
41
+# Legacy links are deprecated since a long time, and starting with v29.0, the Engine won't set legacy links env vars
42
+# automatically in linking containers. Thus, this test is expected to fail — skip it. See https://github.com/moby/moby/pull/50719
43
+PY_TEST_OPTIONS="${PY_TEST_OPTIONS} --deselect=tests/integration/api_container_test.py::CreateContainerTest::test_create_with_links"
44
+
40 45
 (
41 46
 	bundle .integration-daemon-start
42 47
 
... ...
@@ -532,12 +532,3 @@ func (s *DockerCLIExecSuite) TestExecWindowsPathNotWiped(c *testing.T) {
532 532
 	out = strings.ToLower(strings.Trim(out, "\r\n"))
533 533
 	assert.Assert(c, is.Contains(out, `windowspowershell\v1.0`))
534 534
 }
535
-
536
-func (s *DockerCLIExecSuite) TestExecEnvLinksHost(c *testing.T) {
537
-	testRequires(c, DaemonIsLinux)
538
-	runSleepingContainer(c, "-d", "--name", "foo")
539
-	runSleepingContainer(c, "-d", "--link", "foo:db", "--hostname", "myhost", "--name", "bar")
540
-	out := cli.DockerCmd(c, "exec", "bar", "env").Stdout()
541
-	assert.Check(c, is.Contains(out, "HOSTNAME=myhost"))
542
-	assert.Check(c, is.Contains(out, "DB_NAME=/bar/db"))
543
-}
... ...
@@ -193,15 +193,6 @@ func (s *DockerCLILinksSuite) TestLinksUpdateOnRestart(c *testing.T) {
193 193
 	assert.Check(c, is.Equal(ip, realIP))
194 194
 }
195 195
 
196
-func (s *DockerCLILinksSuite) TestLinksEnvs(c *testing.T) {
197
-	testRequires(c, DaemonIsLinux)
198
-	cli.DockerCmd(c, "run", "-d", "-e", "e1=", "-e", "e2=v2", "-e", "e3=v3=v3", "--name=first", "busybox", "top")
199
-	out := cli.DockerCmd(c, "run", "--name=second", "--link=first:first", "busybox", "env").Stdout()
200
-	assert.Assert(c, is.Contains(out, "FIRST_ENV_e1=\n"))
201
-	assert.Assert(c, is.Contains(out, "FIRST_ENV_e2=v2"))
202
-	assert.Assert(c, is.Contains(out, "FIRST_ENV_e3=v3=v3"))
203
-}
204
-
205 196
 func (s *DockerCLILinksSuite) TestLinkShortDefinition(c *testing.T) {
206 197
 	testRequires(c, DaemonIsLinux)
207 198
 	cid := cli.DockerCmd(c, "run", "-d", "--name", "shortlinkdef", "busybox", "top").Stdout()
... ...
@@ -1894,3 +1894,60 @@ func TestDropInForwardChain(t *testing.T) {
1894 1894
 		}
1895 1895
 	})
1896 1896
 }
1897
+
1898
+// TestLegacyLinksEnvVars verify that legacy links environment variables are set in containers when the daemon is
1899
+// started with DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS=1, and are skipped when the daemon is started without that
1900
+// environment variable.
1901
+func TestLegacyLinksEnvVars(t *testing.T) {
1902
+	for _, tc := range []struct {
1903
+		name          string
1904
+		expectEnvVars bool
1905
+	}{
1906
+		{"with legacy links env vars", true},
1907
+		{"without legacy links env vars", false},
1908
+	} {
1909
+		t.Run(tc.name, func(t *testing.T) {
1910
+			var dEnv []string
1911
+			if tc.expectEnvVars {
1912
+				dEnv = []string{"DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS=1"}
1913
+			}
1914
+
1915
+			ctx := setupTest(t)
1916
+			d := daemon.New(t, daemon.WithEnvVars(dEnv...))
1917
+			d.StartWithBusybox(ctx, t)
1918
+			defer d.Stop(t)
1919
+			c := d.NewClientT(t)
1920
+			defer c.Close()
1921
+
1922
+			ctr1 := container.Run(ctx, t, c,
1923
+				container.WithName("ctr1"),
1924
+				container.WithCmd("httpd", "-f"))
1925
+			defer c.ContainerRemove(ctx, ctr1, containertypes.RemoveOptions{Force: true})
1926
+
1927
+			exportRes := container.RunAttach(ctx, t, c,
1928
+				container.WithName("ctr2"),
1929
+				container.WithLinks("ctr1"),
1930
+				container.WithCmd("/bin/sh", "-c", "export"),
1931
+				container.WithAutoRemove)
1932
+
1933
+			// Check the list of environment variables set in the linking container.
1934
+			var found bool
1935
+			for _, l := range strings.Split(exportRes.Stdout.String(), "\n") {
1936
+				if strings.HasPrefix(l, "export CTR1_") {
1937
+					// Legacy links env var found, but not expected.
1938
+					if !tc.expectEnvVars {
1939
+						t.Fatalf("unexpected env var %q", l)
1940
+					}
1941
+
1942
+					// Legacy links env var found, and expected. No need to check further.
1943
+					found = true
1944
+					break
1945
+				}
1946
+			}
1947
+
1948
+			if !found && tc.expectEnvVars {
1949
+				t.Fatal("no legacy links env vars found")
1950
+			}
1951
+		})
1952
+	}
1953
+}