@@ -154,7 +154,7 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
154
154
155
155
opts .Logger (log .LevelInfo , "%s %s - Build development environments from repositories in a container" , newColor (color .Bold ).Sprintf ("envbuilder" ), buildinfo .Version ())
156
156
157
- cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Logger , workingDir , opts .DockerConfigBase64 )
157
+ cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Filesystem , opts . Logger , workingDir , opts .DockerConfigBase64 )
158
158
if err != nil {
159
159
return err
160
160
}
@@ -711,6 +711,11 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
711
711
// Sanitize the environment of any opts!
712
712
options .UnsetEnv ()
713
713
714
+ // Remove the Docker config secret file!
715
+ if err := cleanupDockerConfigOverride (); err != nil {
716
+ return err
717
+ }
718
+
714
719
// Set the environment from /etc/environment first, so it can be
715
720
// overridden by the image and devcontainer settings.
716
721
err = setEnvFromEtcEnvironment (opts .Logger )
@@ -770,11 +775,6 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
770
775
exportEnvFile .Close ()
771
776
}
772
777
773
- // Remove the Docker config secret file!
774
- if err := cleanupDockerConfigOverride (); err != nil {
775
- return err
776
- }
777
-
778
778
if runtimeData .ContainerUser == "" {
779
779
opts .Logger (log .LevelWarn , "#%d: no user specified, using root" , stageNumber )
780
780
}
@@ -978,7 +978,7 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error)
978
978
979
979
opts .Logger (log .LevelInfo , "%s %s - Build development environments from repositories in a container" , newColor (color .Bold ).Sprintf ("envbuilder" ), buildinfo .Version ())
980
980
981
- cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Logger , workingDir , opts .DockerConfigBase64 )
981
+ cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Filesystem , opts . Logger , workingDir , opts .DockerConfigBase64 )
982
982
if err != nil {
983
983
return nil , err
984
984
}
@@ -1571,6 +1571,20 @@ func fileExists(fs billy.Filesystem, path string) bool {
1571
1571
return err == nil
1572
1572
}
1573
1573
1574
+ func readFile (fs billy.Filesystem , name string ) ([]byte , error ) {
1575
+ f , err := fs .Open (name )
1576
+ if err != nil {
1577
+ return nil , fmt .Errorf ("open file: %w" , err )
1578
+ }
1579
+ defer f .Close ()
1580
+
1581
+ b , err := io .ReadAll (f )
1582
+ if err != nil {
1583
+ return nil , fmt .Errorf ("read file: %w" , err )
1584
+ }
1585
+ return b , nil
1586
+ }
1587
+
1574
1588
func copyFile (fs billy.Filesystem , src , dst string , mode fs.FileMode ) error {
1575
1589
srcF , err := fs .Open (src )
1576
1590
if err != nil {
@@ -1595,6 +1609,21 @@ func copyFile(fs billy.Filesystem, src, dst string, mode fs.FileMode) error {
1595
1609
return nil
1596
1610
}
1597
1611
1612
+ func writeFile (fs billy.Filesystem , name string , data []byte , perm fs.FileMode ) error {
1613
+ f , err := fs .OpenFile (name , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , perm )
1614
+ if err != nil {
1615
+ return fmt .Errorf ("create file: %w" , err )
1616
+ }
1617
+ _ , err = f .Write (data )
1618
+ if err != nil {
1619
+ err = fmt .Errorf ("write file: %w" , err )
1620
+ }
1621
+ if err1 := f .Close (); err1 != nil && err == nil {
1622
+ err = fmt .Errorf ("close file: %w" , err1 )
1623
+ }
1624
+ return err
1625
+ }
1626
+
1598
1627
func writeMagicImageFile (fs billy.Filesystem , path string , v any ) error {
1599
1628
file , err := fs .OpenFile (path , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0o644 )
1600
1629
if err != nil {
@@ -1627,39 +1656,45 @@ func parseMagicImageFile(fs billy.Filesystem, path string, v any) error {
1627
1656
return nil
1628
1657
}
1629
1658
1630
- func initDockerConfigOverride (logf log.Func , workingDir workingdir.WorkingDir , dockerConfigBase64 string ) (func () error , error ) {
1631
- var (
1632
- oldDockerConfig = os .Getenv ("DOCKER_CONFIG" )
1633
- newDockerConfig = workingDir .Path ()
1634
- cfgPath = workingDir .Join ("config.json" )
1635
- restoreEnv = func () error { return nil } // noop.
1636
- )
1637
- if dockerConfigBase64 != "" || oldDockerConfig == "" {
1638
- err := os .Setenv ("DOCKER_CONFIG" , newDockerConfig )
1639
- if err != nil {
1640
- logf (log .LevelError , "Failed to set DOCKER_CONFIG: %s" , err )
1641
- return nil , fmt .Errorf ("set DOCKER_CONFIG: %w" , err )
1642
- }
1643
- logf (log .LevelInfo , "Set DOCKER_CONFIG to %s" , newDockerConfig )
1659
+ func initDockerConfigOverride (bfs billy.Filesystem , logf log.Func , workingDir workingdir.WorkingDir , dockerConfigBase64 string ) (func () error , error ) {
1660
+ configFile := "config.json"
1661
+ oldDockerConfig := os .Getenv ("DOCKER_CONFIG" )
1662
+ newDockerConfig := workingDir .Path ()
1644
1663
1645
- restoreEnv = func () error {
1646
- // Restore the old DOCKER_CONFIG value.
1664
+ err := os .Setenv ("DOCKER_CONFIG" , newDockerConfig )
1665
+ if err != nil {
1666
+ logf (log .LevelError , "Failed to set DOCKER_CONFIG: %s" , err )
1667
+ return nil , fmt .Errorf ("set DOCKER_CONFIG: %w" , err )
1668
+ }
1669
+ logf (log .LevelInfo , "Set DOCKER_CONFIG to %s" , newDockerConfig )
1670
+ restoreEnv := onceErrFunc (func () error {
1671
+ // Restore the old DOCKER_CONFIG value.
1672
+ if err := func () error {
1647
1673
if oldDockerConfig == "" {
1648
- err := os .Unsetenv ("DOCKER_CONFIG" )
1649
- if err != nil {
1650
- return fmt .Errorf ("unset DOCKER_CONFIG: %w" , err )
1651
- }
1652
- return nil
1653
- }
1654
- err := os .Setenv ("DOCKER_CONFIG" , oldDockerConfig )
1655
- if err != nil {
1656
- return fmt .Errorf ("restore DOCKER_CONFIG: %w" , err )
1674
+ return os .Unsetenv ("DOCKER_CONFIG" )
1657
1675
}
1658
- logf (log .LevelInfo , "Restored DOCKER_CONFIG to %s" , oldDockerConfig )
1659
- return nil
1676
+ return os .Setenv ("DOCKER_CONFIG" , oldDockerConfig )
1677
+ }(); err != nil {
1678
+ return fmt .Errorf ("restore DOCKER_CONFIG: %w" , err )
1660
1679
}
1661
- } else {
1662
- logf (log .LevelInfo , "Using existing DOCKER_CONFIG set to %s" , oldDockerConfig )
1680
+ logf (log .LevelInfo , "Restored DOCKER_CONFIG to %s" , oldDockerConfig )
1681
+ return nil
1682
+ })
1683
+
1684
+ // If the user hasn't set the BASE64 encoded Docker config, we
1685
+ // should respect the DOCKER_CONFIG environment variable.
1686
+ oldDockerConfigFile := filepath .Join (oldDockerConfig , configFile )
1687
+ if dockerConfigBase64 == "" && fileExists (bfs , oldDockerConfigFile ) {
1688
+ // It's possible that the target file is mounted and needs to
1689
+ // be remounted later, so we should copy the file instead of
1690
+ // hoping that the file is still there when we build.
1691
+ logf (log .LevelInfo , "Using DOCKER_CONFIG at %s" , oldDockerConfig )
1692
+ b , err := readFile (bfs , oldDockerConfigFile )
1693
+ if err != nil {
1694
+ return nil , fmt .Errorf ("read existing DOCKER_CONFIG: %w" , err )
1695
+ }
1696
+ // Read into dockerConfigBase64 so we can keep the same logic.
1697
+ dockerConfigBase64 = base64 .StdEncoding .EncodeToString (b )
1663
1698
}
1664
1699
1665
1700
if dockerConfigBase64 == "" {
@@ -1670,40 +1705,50 @@ func initDockerConfigOverride(logf log.Func, workingDir workingdir.WorkingDir, d
1670
1705
if err != nil {
1671
1706
return restoreEnv , fmt .Errorf ("decode docker config: %w" , err )
1672
1707
}
1673
- var configFile DockerConfig
1674
1708
decoded , err = hujson .Standardize (decoded )
1675
1709
if err != nil {
1676
1710
return restoreEnv , fmt .Errorf ("humanize json for docker config: %w" , err )
1677
1711
}
1678
- err = json .Unmarshal (decoded , & configFile )
1712
+ var dockerConfig DockerConfig
1713
+ err = json .Unmarshal (decoded , & dockerConfig )
1679
1714
if err != nil {
1680
1715
return restoreEnv , fmt .Errorf ("parse docker config: %w" , err )
1681
1716
}
1682
- for k := range configFile .AuthConfigs {
1717
+ for k := range dockerConfig .AuthConfigs {
1683
1718
logf (log .LevelInfo , "Docker config contains auth for registry %q" , k )
1684
1719
}
1685
- err = os .WriteFile (cfgPath , decoded , 0o644 )
1720
+
1721
+ newDockerConfigFile := filepath .Join (newDockerConfig , configFile )
1722
+ err = writeFile (bfs , newDockerConfigFile , decoded , 0o644 )
1686
1723
if err != nil {
1687
1724
return restoreEnv , fmt .Errorf ("write docker config: %w" , err )
1688
1725
}
1689
- logf (log .LevelInfo , "Wrote Docker config JSON to %s" , cfgPath )
1726
+ logf (log .LevelInfo , "Wrote Docker config JSON to %s" , newDockerConfigFile )
1690
1727
1691
- var cleanupOnce sync.Once
1692
- return func () error {
1693
- var cleanupErr error
1694
- cleanupOnce .Do (func () {
1695
- cleanupErr = restoreEnv ()
1696
- // Remove the Docker config secret file!
1697
- if err := os .Remove (cfgPath ); err != nil {
1698
- if ! errors .Is (err , fs .ErrNotExist ) {
1699
- err = errors .Join (err , fmt .Errorf ("remove docker config: %w" , err ))
1700
- }
1701
- logf (log .LevelError , "Failed to remove the Docker config secret file: %s" , cleanupErr )
1702
- cleanupErr = errors .Join (cleanupErr , err )
1728
+ cleanup := onceErrFunc (func () error {
1729
+ err := restoreEnv ()
1730
+ // Remove the Docker config secret file!
1731
+ if err2 := os .Remove (newDockerConfigFile ); err2 != nil {
1732
+ if ! errors .Is (err2 , fs .ErrNotExist ) {
1733
+ err2 = fmt .Errorf ("remove docker config: %w" , err2 )
1703
1734
}
1735
+ logf (log .LevelError , "Failed to remove the Docker config secret file: %s" , err2 )
1736
+ err = errors .Join (err , err2 )
1737
+ }
1738
+ return err
1739
+ })
1740
+ return cleanup , nil
1741
+ }
1742
+
1743
+ func onceErrFunc (f func () error ) func () error {
1744
+ var once sync.Once
1745
+ return func () error {
1746
+ var err error
1747
+ once .Do (func () {
1748
+ err = f ()
1704
1749
})
1705
- return cleanupErr
1706
- }, nil
1750
+ return err
1751
+ }
1707
1752
}
1708
1753
1709
1754
// Allows quick testing of layer caching using a local directory!
0 commit comments