1
1
package docker
2
2
3
3
import (
4
- "archive/tar"
5
4
"bytes"
6
5
"context"
7
6
"io"
8
7
"io/ioutil"
9
8
"net"
10
9
"os"
10
+ "path"
11
+ "path/filepath"
11
12
"time"
12
13
13
14
"github.com/docker/buildx/driver"
14
15
"github.com/docker/buildx/driver/bkimage"
15
16
"github.com/docker/buildx/util/imagetools"
16
17
"github.com/docker/buildx/util/progress"
18
+ "github.com/docker/cli/cli/command"
17
19
"github.com/docker/docker/api/types"
18
20
dockertypes "github.com/docker/docker/api/types"
19
21
"github.com/docker/docker/api/types/container"
20
22
"github.com/docker/docker/api/types/mount"
21
23
"github.com/docker/docker/api/types/network"
22
24
dockerclient "github.com/docker/docker/client"
25
+ dockerarchive "github.com/docker/docker/pkg/archive"
23
26
"github.com/docker/docker/pkg/stdcopy"
27
+ "github.com/docker/docker/pkg/system"
24
28
"github.com/moby/buildkit/client"
29
+ buildkitconfig "github.com/moby/buildkit/cmd/buildkitd/config"
25
30
"github.com/moby/buildkit/util/tracing/detect"
26
31
"github.com/pkg/errors"
27
32
)
@@ -130,11 +135,10 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
130
135
return err
131
136
}
132
137
if f := d .InitConfig .ConfigFile ; f != "" {
133
- buf , err := readFileToTar (f )
134
- if err != nil {
138
+ if err := d .copyToContainer (ctx , f , "/" ); err != nil {
135
139
return err
136
140
}
137
- if err := d .DockerAPI . CopyToContainer (ctx , d . Name , "/" , buf , dockertypes. CopyToContainerOptions {} ); err != nil {
141
+ if err := d .copyRegCertsToContainer (ctx , f ); err != nil {
138
142
return err
139
143
}
140
144
}
@@ -196,6 +200,126 @@ func (d *Driver) copyLogs(ctx context.Context, l progress.SubLogger) error {
196
200
return rc .Close ()
197
201
}
198
202
203
+ // copyToContainer is based on the implementation from docker/cli
204
+ // https://github.com/docker/cli/blob/master/cli/command/container/cp.go
205
+ func (d * Driver ) copyToContainer (ctx context.Context , srcPath string , dstPath string ) error {
206
+ var err error
207
+
208
+ // Get an absolute source path.
209
+ srcPath , err = resolveLocalPath (srcPath )
210
+ if err != nil {
211
+ return err
212
+ }
213
+
214
+ // Prepare the destination.
215
+ dstInfo := dockerarchive.CopyInfo {Path : dstPath }
216
+ dstStat , err := d .DockerAPI .ContainerStatPath (ctx , d .Name , dstPath )
217
+
218
+ // If the destination is a symbolic link, we should evaluate it.
219
+ if err == nil && dstStat .Mode & os .ModeSymlink != 0 {
220
+ linkTarget := dstStat .LinkTarget
221
+ if ! system .IsAbs (linkTarget ) {
222
+ dstParent , _ := dockerarchive .SplitPathDirEntry (dstPath )
223
+ linkTarget = filepath .Join (dstParent , linkTarget )
224
+ }
225
+ dstInfo .Path = linkTarget
226
+ dstStat , err = d .DockerAPI .ContainerStatPath (ctx , d .Name , linkTarget )
227
+ }
228
+
229
+ // Validate the destination path.
230
+ if err := command .ValidateOutputPathFileMode (dstStat .Mode ); err != nil {
231
+ return errors .Wrapf (err , `destination "%s:%s" must be a directory or a regular file` , d .Name , dstPath )
232
+ }
233
+
234
+ // Ignore any error and assume that the parent directory of the destination
235
+ // path exists, in which case the copy may still succeed. If there is any
236
+ // type of conflict (e.g., non-directory overwriting an existing directory
237
+ // or vice versa) the extraction will fail. If the destination simply did
238
+ // not exist, but the parent directory does, the extraction will still
239
+ // succeed.
240
+ dstInfo .Exists , dstInfo .IsDir = true , dstStat .Mode .IsDir ()
241
+
242
+ // Prepare source copy info.
243
+ srcInfo , err := dockerarchive .CopyInfoSourcePath (srcPath , true )
244
+ if err != nil {
245
+ return err
246
+ }
247
+
248
+ srcArchive , err := dockerarchive .TarResource (srcInfo )
249
+ if err != nil {
250
+ return err
251
+ }
252
+ defer srcArchive .Close ()
253
+
254
+ // With the stat info about the local source as well as the
255
+ // destination, we have enough information to know whether we need to
256
+ // alter the archive that we upload so that when the server extracts
257
+ // it to the specified directory in the container we get the desired
258
+ // copy behavior.
259
+
260
+ // See comments in the implementation of `dockerarchive.PrepareArchiveCopy`
261
+ // for exactly what goes into deciding how and whether the source
262
+ // archive needs to be altered for the correct copy behavior when it is
263
+ // extracted. This function also infers from the source and destination
264
+ // info which directory to extract to, which may be the parent of the
265
+ // destination that the user specified.
266
+ dstDir , preparedArchive , err := dockerarchive .PrepareArchiveCopy (srcArchive , srcInfo , dstInfo )
267
+ if err != nil {
268
+ return err
269
+ }
270
+ defer preparedArchive .Close ()
271
+
272
+ return d .DockerAPI .CopyToContainer (ctx , d .Name , dstDir , preparedArchive , dockertypes.CopyToContainerOptions {
273
+ AllowOverwriteDirWithFile : false ,
274
+ })
275
+ }
276
+
277
+ func resolveLocalPath (localPath string ) (absPath string , err error ) {
278
+ if absPath , err = filepath .Abs (localPath ); err != nil {
279
+ return
280
+ }
281
+ return dockerarchive .PreserveTrailingDotOrSeparator (absPath , localPath , filepath .Separator ), nil
282
+ }
283
+
284
+ func (d * Driver ) copyRegCertsToContainer (ctx context.Context , bkconfig string ) error {
285
+ // Load BuildKit config.
286
+ bcfg , err := buildkitconfig .LoadFile (bkconfig )
287
+ if err != nil {
288
+ return err
289
+ }
290
+
291
+ // Create a temp folder and copy all certs to it with copyFileParents
292
+ // to keep the exact same folder structure as defined in BuildKit config.
293
+ // It's required to do that here because we cannot copy a file in a folder
294
+ // that does not exist in the container with the CopyToContainer API.
295
+ tmpRegCerts , err := os .MkdirTemp ("" , "basedir-certs" )
296
+ if err != nil {
297
+ return err
298
+ }
299
+ defer os .RemoveAll (tmpRegCerts )
300
+ for _ , reg := range bcfg .Registries {
301
+ for _ , rootCA := range reg .RootCAs {
302
+ if err := copyFileParents (rootCA , tmpRegCerts ); err != nil {
303
+ return err
304
+ }
305
+ }
306
+ for _ , keyPair := range reg .KeyPairs {
307
+ if len (keyPair .Key ) > 0 {
308
+ if err := copyFileParents (keyPair .Key , tmpRegCerts ); err != nil {
309
+ return err
310
+ }
311
+ }
312
+ if len (keyPair .Certificate ) > 0 {
313
+ if err := copyFileParents (keyPair .Certificate , tmpRegCerts ); err != nil {
314
+ return err
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ return d .copyToContainer (ctx , tmpRegCerts + "/." , "/" )
321
+ }
322
+
199
323
func (d * Driver ) exec (ctx context.Context , cmd []string ) (string , net.Conn , error ) {
200
324
execConfig := types.ExecConfig {
201
325
Cmd : cmd ,
@@ -357,29 +481,6 @@ func (d *demux) Read(dt []byte) (int, error) {
357
481
return d .Reader .Read (dt )
358
482
}
359
483
360
- func readFileToTar (fn string ) (* bytes.Buffer , error ) {
361
- buf := bytes .NewBuffer (nil )
362
- tw := tar .NewWriter (buf )
363
- dt , err := ioutil .ReadFile (fn )
364
- if err != nil {
365
- return nil , err
366
- }
367
- if err := tw .WriteHeader (& tar.Header {
368
- Name : "/etc/buildkit/buildkitd.toml" ,
369
- Size : int64 (len (dt )),
370
- Mode : 0644 ,
371
- }); err != nil {
372
- return nil , err
373
- }
374
- if _ , err := tw .Write (dt ); err != nil {
375
- return nil , err
376
- }
377
- if err := tw .Close (); err != nil {
378
- return nil , err
379
- }
380
- return buf , nil
381
- }
382
-
383
484
type logWriter struct {
384
485
logger progress.SubLogger
385
486
stream int
@@ -389,3 +490,53 @@ func (l *logWriter) Write(dt []byte) (int, error) {
389
490
l .logger .Log (l .stream , dt )
390
491
return len (dt ), nil
391
492
}
493
+
494
+ // copyFileParents copies a file using the full source file name under a
495
+ // folder. For example, copying /bar/foo.txt to /tmp will result in
496
+ // the file being placed in /tmp/bar/foo.txt.
497
+ func copyFileParents (srcFile string , destFolder string ) error {
498
+ si , err := os .Stat (srcFile )
499
+ if err != nil {
500
+ return err
501
+ }
502
+
503
+ pi , err := os .Stat (path .Dir (srcFile ))
504
+ if err != nil {
505
+ return err
506
+ }
507
+
508
+ tdd := path .Join (destFolder , path .Dir (srcFile ))
509
+ if err := os .MkdirAll (tdd , pi .Mode ()); err != nil {
510
+ return err
511
+ }
512
+
513
+ if si .Mode ()& os .ModeSymlink != 0 {
514
+ if srcFile , err = os .Readlink (srcFile ); err != nil {
515
+ return err
516
+ }
517
+ si , err = os .Stat (srcFile )
518
+ if err != nil {
519
+ return err
520
+ }
521
+ }
522
+
523
+ sf , err := os .Open (srcFile )
524
+ if err != nil {
525
+ return err
526
+ }
527
+ defer sf .Close ()
528
+
529
+ tdf := path .Join (tdd , path .Base (srcFile ))
530
+ df , err := os .Create (tdf )
531
+ if err != nil {
532
+ return err
533
+ }
534
+ defer df .Close ()
535
+
536
+ _ , err = io .Copy (df , sf )
537
+ if err != nil {
538
+ return err
539
+ }
540
+
541
+ return os .Chmod (tdf , si .Mode ())
542
+ }
0 commit comments