-
Notifications
You must be signed in to change notification settings - Fork 862
Description
Desired behavior:
I would expect that when no RUN commands are present in a container file, buildah build --os=windows would be able to build a working Windows container image.
Current state:
In my testing I have found that buildah does not create functional Windows container images.
This seems to be due to at least two reasons:
- The required tar headers specified by the OCI image-spec are not being applied.
(image-spec issue with slightly more info) - The rootfs of Windows container images is actually the
Files/directory. This can be seen by pulling a Windows container image and inspecting the layers.
There may be other reasons, but if there are, they are not documented.
I have only tested this by building an image with a Linux build of buildah, with the image being used by containerd on a Windows VM.
I built two images, one using docker buildx, the other with buildah:
Using the following containerfile:
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
ADD emptyfile .
An image built using docker buildx works with no issue, containerd logs:
time="2022-05-20T16:46:52.533976500+02:00" level=info msg="PullImage \"quay.io/ssoto/test:nanoBuildx\""
time="2022-05-20T16:47:07.411314400+02:00" level=info msg="ImageCreate event &ImageCreate{Name:quay.io/ssoto/test:nanoBuildx,Labels:map[string]string{io.cri-containerd.image: managed,},XXX_unrecognized:[],}"
time="2022-05-20T16:47:07.422273200+02:00" level=info msg="ImageCreate event &ImageCreate{Name:sha256:43aba2088aa29238b8dc6ce66f9fdea52bc9401da781c03dc7c0bbfb5e9b0b67,Labels:map[string]string{io.cri-containerd.image: managed,},XXX_unrecognized:[],}"
time="2022-05-20T16:47:07.430670500+02:00" level=info msg="ImageUpdate event &ImageUpdate{Name:quay.io/ssoto/test:nanoBuildx,Labels:map[string]string{io.cri-containerd.image: managed,},XXX_unrecognized:[],}"
time="2022-05-20T16:47:07.438064300+02:00" level=info msg="ImageCreate event &ImageCreate{Name:quay.io/ssoto/test@sha256:fab9aeb28d8b11c65595143fcfb80a44a4f1a8d917b5559be981d8353011fde1,Labels:map[string]string{io.cri-containerd.image: managed,},XXX_unrecognized:[],}"
time="2022-05-20T16:47:07.438593200+02:00" level=info msg="PullImage \"quay.io/ssoto/test:nanoBuildx\" returns image reference \"sha256:43aba2088aa29238b8dc6ce66f9fdea52bc9401da781c03dc7c0bbfb5e9b0b67\""
buildah built images cause the following in containerd logs when pulling the image:
time="2022-05-20T16:41:34.956100200+02:00" level=info msg="PullImage \"quay.io/ssoto/test:nanoShouldWork\""
time="2022-05-20T16:41:50.274201300+02:00" level=error msg="PullImage \"quay.io/ssoto/test:nanoShouldWork\" failed" error="failed to pull and unpack image \"quay.io/ssoto/test:nanoShouldWork\": failed to extract layer sha256:dff44eadebe5e0f7717289f95e61260f45df7951a5fc2adea46801dcded8ae6d: hcsshim::ImportLayer failed in Win32: The system cannot find the path specified. (0x3): unknown"
The difference between the two images can be seen in the image layer created from the ADD directive
buildkit added layer:
header: &{Typeflag:53 Name:Hives Linkname: Size:0 Mode:16384 Uid:0 Gid:0 Uname: Gname: ModTime:2022-05-20 10:45:06.607995006 -0400 EDT AccessTime:0001-01-01 00:00:00 +0000 UTC ChangeTime:0001-01-01 00:00:00 +0000 UTC Devmajor:0 Devminor:0 Xattrs:map[] PAXRecords:map[LIBARCHIVE.creationtime:1653057906.607995006 MSWIND
OWS.fileattr:16 mtime:1653057906.607995006] Format:PAX}
header: &{Typeflag:53 Name:Files Linkname: Size:0 Mode:16384 Uid:0 Gid:0 Uname: Gname: ModTime:2022-05-20 10:45:06.60847413 -0400 EDT AccessTime:0001-01-01 00:00:00 +0000 UTC ChangeTime:0001-01-01 00:00:00 +0000 UTC Devmajor:0 Devminor:0 Xattrs:map[] PAXRecords:map[LIBARCHIVE.creationtime:1653057906.608474130 MSWINDO
WS.fileattr:16 mtime:1653057906.60847413] Format:PAX}
header: &{Typeflag:48 Name:Files/emptyfile Linkname: Size:0 Mode:33204 Uid:0 Gid:0 Uname:root Gname:root ModTime:2022-05-20 09:46:28 -0400 EDT AccessTime:0001-01-01 00:00:00 +0000 UTC ChangeTime:0001-01-01 00:00:00 +0000 UTC Devmajor:0 Devminor:0 Xattrs:map[] PAXRecords:map[LIBARCHIVE.creationtime:1653054388.0 MSWIND
OWS.fileattr:32 MSWINDOWS.rawsd:AQAEgBQAAAAkAAAAAAAAADAAAAABAgAAAAAABSAAAAAgAgAAAQEAAAAAAAUSAAAAAgBMAAMAAAAAABgA/wEfAAECAAAAAAAFIAAAACACAAAAABQA/wEfAAEBAAAAAAAFEgAAAAAAGACpABIAAQIAAAAAAAUgAAAAIQIAAA==] Format:PAX}
buildah added layer:
header: &{Typeflag:48 Name:emptyfile Linkname: Size:0 Mode:33204 Uid:0 Gid:0 Uname:root Gname:root ModTime:2022-05-20 09:46:28 -0400 EDT AccessTime:0001-01-01 00:00:00 +0000 UTC ChangeTime:0001-01-01 00:00:00 +0000 UTC Devmajor:0 Devminor:0 Xattrs:map[] PAXRecords:map[] Format:USTAR}
What I've tried:
I've done some hacking on add.go, to try and see the minimal changes possible to make things work, mainly around to add Windows expected headers to a new directory I'm creating.
diff --git a/add.go b/add.go
index 8aa53a29..66032170 100644
--- a/add.go
+++ b/add.go
@@ -376,9 +376,66 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
if err := copier.Mkdir(mountPoint, extractDirectory, mkdirOptions); err != nil {
return errors.Wrapf(err, "error ensuring target directory exists")
}
+ ////////
+ {
+ logrus.Infof("starting creating some directories")
+ var putErr error
+ var wg sync.WaitGroup
+ pipeReader, pipeWriter := io.Pipe()
+ wg.Add(1)
+ go func() {
+ tw := tar.NewWriter(pipeWriter)
+ defer tw.Close()
+ hdr := tar.Header{
+ Typeflag: tar.TypeDir,
+ Name: "testdir",
+ Size: 0,
+ Format: tar.FormatPAX,
+ Mode: 1 << 14,
+ PAXRecords: map[string]string{
+ "MSWINDOWS.fileattr": "16",
+ "MSWINDOWS.rawsd": "",
+ },
+ }
+ err = tw.WriteHeader(&hdr)
+ if err != nil {
+ logrus.Infof("error %s", err)
+ }
+ pipeWriter.Close()
+ wg.Done()
+ }()
+ wg.Add(1)
+ go func() {
+ b.ContentDigester.Start("")
+ hashCloser := b.ContentDigester.Hash()
+ hasher := io.Writer(hashCloser)
+ if options.Hasher != nil {
+ hasher = io.MultiWriter(hasher, options.Hasher)
+ }
+ if options.DryRun {
+ _, putErr = io.Copy(hasher, pipeReader)
+ } else {
+ putOptions := copier.PutOptions{
+ UIDMap: destUIDMap,
+ GIDMap: destGIDMap,
+ ChownDirs: nil,
+ ChmodDirs: nil,
+ ChownFiles: nil,
+ ChmodFiles: nil,
+ IgnoreDevices: userns.RunningInUserNS(),
+ }
+ putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
+ }
+ hashCloser.Close()
+ pipeReader.Close()
+ wg.Done()
+ }()
+ }
+ ///////
Built and saved the resulting image
make buildah
./bin/buildah build --log-level=info -f Dockerfile.nano -t handmade:latest .
./bin/buildah push handmade:latest dir:handmade
For a reason unknown to me, I can't get the header changes to stick. I have some ideas as to why this is happening but I'm not familiar enough with this codebase to fully figure this out.
Extra info:
What buildkit is doing differently from buildah
Related containerd issue: https://github.com/containerd/containerd/issues/2469