@@ -1710,11 +1710,30 @@ func testFrontendImageNaming(t *testing.T, sb integration.Sandbox) {
17101710}
17111711
17121712func testSecretMounts (t * testing.T , sb integration.Sandbox ) {
1713- integration .SkipOnPlatform (t , "windows" )
1713+ // https://docs.docker.com/engine/swarm/secrets/
1714+
1715+ // Windows vs Linux secret implementation differences:
1716+ //
1717+ // Linux: Secrets are mounted as files using tmpfs at /run/secrets/ (RAM-based, encrypted)
1718+ //
1719+ // Windows: Secrets are stored in C:\ProgramData\Docker\internal\secrets (clear text on disk)
1720+ // Symbolic links point to the desired target (default: C:\ProgramData\Docker\secrets)
1721+ // Windows does NOT support tmpfs or non-directory file bind-mounts
1722+ // UID/GID/mode options are NOT supported on Windows
1723+ // Recommend BitLocker for at-rest encryption
1724+ //
1725+ // BuildKit Issue: The "invalid windows mount type: 'tmpfs'" error occurs because BuildKit
1726+ // currently tries to use tmpfs for all secret mounts. This needs to be fixed in BuildKit's
1727+ // secret mount implementation to use the Windows symlink approach instead.
1728+
1729+ // For now, this test is Linux-only until BuildKit properly implements Windows secret mounts
1730+ // without tmpfs. Use testSecretEnv for Windows-compatible environment-based secrets.
1731+ integration .SkipOnPlatform (t , "windows" , "Windows does not support tmpfs for secret mounts" )
17141732 c , err := New (sb .Context (), sb .Address ())
17151733 require .NoError (t , err )
17161734 defer c .Close ()
17171735
1736+ // Test 1: Basic secret mount with content verification (Linux only)
17181737 st := llb .Image ("busybox:latest" ).
17191738 Run (llb .Shlex (`sh -c 'mount | grep mysecret | grep "type tmpfs" && [ "$(cat /run/secrets/mysecret)" = 'foo-secret' ]'` ), llb .AddSecret ("/run/secrets/mysecret" ))
17201739
@@ -1728,7 +1747,7 @@ func testSecretMounts(t *testing.T, sb integration.Sandbox) {
17281747 }, nil )
17291748 require .NoError (t , err )
17301749
1731- // test optional, mount should not exist when secret not present in SolveOpt
1750+ // Test 2: Optional secret - mount should not exist when secret not present in SolveOpt
17321751 st = llb .Image ("busybox:latest" ).
17331752 Run (llb .Shlex (`test ! -f /run/secrets/mysecret2` ), llb .AddSecret ("/run/secrets/mysecret2" , llb .SecretOptional ))
17341753
@@ -1743,6 +1762,7 @@ func testSecretMounts(t *testing.T, sb integration.Sandbox) {
17431762 _ , err = c .Solve (sb .Context (), def , SolveOpt {}, nil )
17441763 require .NoError (t , err )
17451764
1765+ // Test 3: Required secret missing - should error
17461766 st = llb .Image ("busybox:latest" ).
17471767 Run (llb .Shlex (`echo secret3` ), llb .AddSecret ("/run/secrets/mysecret3" ))
17481768
@@ -1754,7 +1774,7 @@ func testSecretMounts(t *testing.T, sb integration.Sandbox) {
17541774 }, nil )
17551775 require .Error (t , err )
17561776
1757- // test id,perm,uid
1777+ // Test 4: Secret with custom ID and file permissions
17581778 st = llb .Image ("busybox:latest" ).
17591779 Run (llb .Shlex (`sh -c '[ "$(stat -c "%u %g %f" /run/secrets/mysecret4)" = "1 1 81ff" ]' ` ), llb .AddSecret ("/run/secrets/mysecret4" , llb .SecretID ("mysecret" ), llb .SecretFileOpt (1 , 1 , 0777 )))
17601780
@@ -1768,7 +1788,7 @@ func testSecretMounts(t *testing.T, sb integration.Sandbox) {
17681788 }, nil )
17691789 require .NoError (t , err )
17701790
1771- // test empty cert still creates secret file
1791+ // Test 5: Empty secret still creates secret file
17721792 st = llb .Image ("busybox:latest" ).
17731793 Run (llb .Shlex (`test -f /run/secrets/mysecret5` ), llb .AddSecret ("/run/secrets/mysecret5" , llb .SecretID ("mysecret" )))
17741794
@@ -1784,13 +1804,22 @@ func testSecretMounts(t *testing.T, sb integration.Sandbox) {
17841804}
17851805
17861806func testSecretEnv (t * testing.T , sb integration.Sandbox ) {
1787- integration .SkipOnPlatform (t , "windows" )
17881807 c , err := New (sb .Context (), sb .Address ())
17891808 require .NoError (t , err )
17901809 defer c .Close ()
17911810
1792- st := llb .Image ("busybox:latest" ).
1793- Run (llb .Shlex (`sh -c '[ "$(echo ${MY_SECRET})" = 'foo-secret' ]'` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true )))
1811+ imgName := integration .UnixOrWindows ("busybox:latest" , "nanoserver:latest" )
1812+ var st llb.ExecState
1813+
1814+ // Test 1: Verify secret value is accessible as environment variable
1815+ switch imgName {
1816+ case "nanoserver:latest" :
1817+ st = llb .Image (imgName ).
1818+ Run (llb .Shlex (`cmd /C "if "%MY_SECRET%"=="foo-secret" (exit 0) else (exit 1)"` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true )))
1819+ case "busybox:latest" :
1820+ st = llb .Image (imgName ).
1821+ Run (llb .Shlex (`sh -c '[ "$(echo ${MY_SECRET})" = 'foo-secret' ]'` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true )))
1822+ }
17941823
17951824 def , err := st .Marshal (sb .Context ())
17961825 require .NoError (t , err )
@@ -1802,9 +1831,15 @@ func testSecretEnv(t *testing.T, sb integration.Sandbox) {
18021831 }, nil )
18031832 require .NoError (t , err )
18041833
1805- // test optional
1806- st = llb .Image ("busybox:latest" ).
1807- Run (llb .Shlex (`sh -c '[ -z "${MY_SECRET}" ]'` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true ), llb .SecretOptional ))
1834+ // Test 2: Optional secret not provided should be unset/empty
1835+ switch imgName {
1836+ case "nanoserver:latest" :
1837+ st = llb .Image (imgName ).
1838+ Run (llb .Shlex (`cmd /C "if not defined MY_SECRET (exit 0) else (exit 1)"` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true ), llb .SecretOptional ))
1839+ case "busybox:latest" :
1840+ st = llb .Image (imgName ).
1841+ Run (llb .Shlex (`sh -c '[ -z "${MY_SECRET}" ]'` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true ), llb .SecretOptional ))
1842+ }
18081843
18091844 def , err = st .Marshal (sb .Context ())
18101845 require .NoError (t , err )
@@ -1817,8 +1852,15 @@ func testSecretEnv(t *testing.T, sb integration.Sandbox) {
18171852 _ , err = c .Solve (sb .Context (), def , SolveOpt {}, nil )
18181853 require .NoError (t , err )
18191854
1820- st = llb .Image ("busybox:latest" ).
1821- Run (llb .Shlex (`echo foo` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true )))
1855+ // Test 3: Required secret not provided should error
1856+ switch imgName {
1857+ case "nanoserver:latest" :
1858+ st = llb .Image (imgName ).
1859+ Run (llb .Shlex (`cmd /C "echo foo"` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true )))
1860+ case "busybox:latest" :
1861+ st = llb .Image (imgName ).
1862+ Run (llb .Shlex (`echo foo` ), llb .AddSecret ("MY_SECRET" , llb .SecretAsEnv (true )))
1863+ }
18221864
18231865 def , err = st .Marshal (sb .Context ())
18241866 require .NoError (t , err )
@@ -1828,12 +1870,21 @@ func testSecretEnv(t *testing.T, sb integration.Sandbox) {
18281870 }, nil )
18291871 require .Error (t , err )
18301872
1831- // test id
1832- st = llb .Image ("busybox:latest" ).
1833- Run (llb .Shlex (`sh -c '[ "$(echo ${MYPASSWORD}-${MYTOKEN})" = "pw-token" ]' ` ),
1834- llb .AddSecret ("MYPASSWORD" , llb .SecretID ("pass" ), llb .SecretAsEnv (true )),
1835- llb .AddSecret ("MYTOKEN" , llb .SecretAsEnv (true )),
1836- )
1873+ // Test 4: Multiple secrets with custom IDs
1874+ switch imgName {
1875+ case "nanoserver:latest" :
1876+ st = llb .Image (imgName ).
1877+ Run (llb .Shlex (`cmd /C "if "%MYPASSWORD%-%MYTOKEN%"=="pw-token" (exit 0) else (exit 1)"` ),
1878+ llb .AddSecret ("MYPASSWORD" , llb .SecretID ("pass" ), llb .SecretAsEnv (true )),
1879+ llb .AddSecret ("MYTOKEN" , llb .SecretAsEnv (true )),
1880+ )
1881+ case "busybox:latest" :
1882+ st = llb .Image (imgName ).
1883+ Run (llb .Shlex (`sh -c '[ "$(echo ${MYPASSWORD}-${MYTOKEN})" = "pw-token" ]' ` ),
1884+ llb .AddSecret ("MYPASSWORD" , llb .SecretID ("pass" ), llb .SecretAsEnv (true )),
1885+ llb .AddSecret ("MYTOKEN" , llb .SecretAsEnv (true )),
1886+ )
1887+ }
18371888
18381889 def , err = st .Marshal (sb .Context ())
18391890 require .NoError (t , err )
@@ -5125,7 +5176,7 @@ func testBuildExportWithUncompressed(t *testing.T, sb integration.Sandbox) {
51255176}
51265177
51275178func testBuildExportZstd (t * testing.T , sb integration.Sandbox ) {
5128- integration .SkipOnPlatform (t , "windows" )
5179+ integration .SkipOnPlatform (t , "windows" , "Windows container support incomplete: AddMount() fails with 'number of mounts should always be 1 for Windows layers', OCI export fails with 'windowsLcowDiff does not implement Compare method'" )
51295180 workers .CheckFeatureCompat (t , sb , workers .FeatureOCIExporter )
51305181 c , err := New (sb .Context (), sb .Address ())
51315182 require .NoError (t , err )
0 commit comments