@@ -154,7 +154,7 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
154154
155155 opts .Logger (log .LevelInfo , "%s %s - Build development environments from repositories in a container" , newColor (color .Bold ).Sprintf ("envbuilder" ), buildinfo .Version ())
156156
157- cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Logger , workingDir , opts .DockerConfigBase64 )
157+ cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Filesystem , opts . Logger , workingDir , opts .DockerConfigBase64 )
158158 if err != nil {
159159 return err
160160 }
@@ -711,6 +711,11 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
711711 // Sanitize the environment of any opts!
712712 options .UnsetEnv ()
713713
714+ // Remove the Docker config secret file!
715+ if err := cleanupDockerConfigOverride (); err != nil {
716+ return err
717+ }
718+
714719 // Set the environment from /etc/environment first, so it can be
715720 // overridden by the image and devcontainer settings.
716721 err = setEnvFromEtcEnvironment (opts .Logger )
@@ -770,11 +775,6 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
770775 exportEnvFile .Close ()
771776 }
772777
773- // Remove the Docker config secret file!
774- if err := cleanupDockerConfigOverride (); err != nil {
775- return err
776- }
777-
778778 if runtimeData .ContainerUser == "" {
779779 opts .Logger (log .LevelWarn , "#%d: no user specified, using root" , stageNumber )
780780 }
@@ -978,7 +978,7 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error)
978978
979979 opts .Logger (log .LevelInfo , "%s %s - Build development environments from repositories in a container" , newColor (color .Bold ).Sprintf ("envbuilder" ), buildinfo .Version ())
980980
981- cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Logger , workingDir , opts .DockerConfigBase64 )
981+ cleanupDockerConfigOverride , err := initDockerConfigOverride (opts .Filesystem , opts . Logger , workingDir , opts .DockerConfigBase64 )
982982 if err != nil {
983983 return nil , err
984984 }
@@ -1571,6 +1571,20 @@ func fileExists(fs billy.Filesystem, path string) bool {
15711571 return err == nil
15721572}
15731573
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+
15741588func copyFile (fs billy.Filesystem , src , dst string , mode fs.FileMode ) error {
15751589 srcF , err := fs .Open (src )
15761590 if err != nil {
@@ -1595,6 +1609,21 @@ func copyFile(fs billy.Filesystem, src, dst string, mode fs.FileMode) error {
15951609 return nil
15961610}
15971611
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+
15981627func writeMagicImageFile (fs billy.Filesystem , path string , v any ) error {
15991628 file , err := fs .OpenFile (path , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0o644 )
16001629 if err != nil {
@@ -1627,39 +1656,45 @@ func parseMagicImageFile(fs billy.Filesystem, path string, v any) error {
16271656 return nil
16281657}
16291658
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 ()
16441663
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 {
16471673 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" )
16571675 }
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 )
16601679 }
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 )
16631698 }
16641699
16651700 if dockerConfigBase64 == "" {
@@ -1670,40 +1705,50 @@ func initDockerConfigOverride(logf log.Func, workingDir workingdir.WorkingDir, d
16701705 if err != nil {
16711706 return restoreEnv , fmt .Errorf ("decode docker config: %w" , err )
16721707 }
1673- var configFile DockerConfig
16741708 decoded , err = hujson .Standardize (decoded )
16751709 if err != nil {
16761710 return restoreEnv , fmt .Errorf ("humanize json for docker config: %w" , err )
16771711 }
1678- err = json .Unmarshal (decoded , & configFile )
1712+ var dockerConfig DockerConfig
1713+ err = json .Unmarshal (decoded , & dockerConfig )
16791714 if err != nil {
16801715 return restoreEnv , fmt .Errorf ("parse docker config: %w" , err )
16811716 }
1682- for k := range configFile .AuthConfigs {
1717+ for k := range dockerConfig .AuthConfigs {
16831718 logf (log .LevelInfo , "Docker config contains auth for registry %q" , k )
16841719 }
1685- err = os .WriteFile (cfgPath , decoded , 0o644 )
1720+
1721+ newDockerConfigFile := filepath .Join (newDockerConfig , configFile )
1722+ err = writeFile (bfs , newDockerConfigFile , decoded , 0o644 )
16861723 if err != nil {
16871724 return restoreEnv , fmt .Errorf ("write docker config: %w" , err )
16881725 }
1689- logf (log .LevelInfo , "Wrote Docker config JSON to %s" , cfgPath )
1726+ logf (log .LevelInfo , "Wrote Docker config JSON to %s" , newDockerConfigFile )
16901727
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 )
17031734 }
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 ()
17041749 })
1705- return cleanupErr
1706- }, nil
1750+ return err
1751+ }
17071752}
17081753
17091754// Allows quick testing of layer caching using a local directory!
0 commit comments