1package ops 2 3import ( 4 "context" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "path/filepath" 9 10 "github.com/containerd/containerd/mount" 11 "github.com/containerd/containerd/platforms" 12 "github.com/docker/docker/pkg/idtools" 13 "github.com/moby/buildkit/snapshot" 14 "github.com/moby/buildkit/solver/pb" 15 "github.com/moby/buildkit/util/archutil" 16 specs "github.com/opencontainers/image-spec/specs-go/v1" 17 "github.com/pkg/errors" 18 copy "github.com/tonistiigi/fsutil/copy" 19) 20 21const qemuMountName = "/dev/.buildkit_qemu_emulator" 22 23var qemuArchMap = map[string]string{ 24 "arm64": "aarch64", 25 "amd64": "x86_64", 26 "riscv64": "riscv64", 27 "arm": "arm", 28 "s390x": "s390x", 29 "ppc64le": "ppc64le", 30 "386": "i386", 31} 32 33type emulator struct { 34 path string 35 idmap *idtools.IdentityMapping 36} 37 38func (e *emulator) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) { 39 return &staticEmulatorMount{path: e.path, idmap: e.idmap}, nil 40} 41 42type staticEmulatorMount struct { 43 path string 44 idmap *idtools.IdentityMapping 45} 46 47func (m *staticEmulatorMount) Mount() ([]mount.Mount, func() error, error) { 48 tmpdir, err := ioutil.TempDir("", "buildkit-qemu-emulator") 49 if err != nil { 50 return nil, nil, err 51 } 52 var ret bool 53 defer func() { 54 if !ret { 55 os.RemoveAll(tmpdir) 56 } 57 }() 58 59 var uid, gid int 60 if m.idmap != nil { 61 root := m.idmap.RootPair() 62 uid = root.UID 63 gid = root.GID 64 } 65 if err := copy.Copy(context.TODO(), filepath.Dir(m.path), filepath.Base(m.path), tmpdir, qemuMountName, func(ci *copy.CopyInfo) { 66 m := 0555 67 ci.Mode = &m 68 }, copy.WithChown(uid, gid)); err != nil { 69 return nil, nil, err 70 } 71 72 ret = true 73 return []mount.Mount{{ 74 Type: "bind", 75 Source: filepath.Join(tmpdir, qemuMountName), 76 Options: []string{"ro", "bind"}, 77 }}, func() error { 78 return os.RemoveAll(tmpdir) 79 }, nil 80 81} 82func (m *staticEmulatorMount) IdentityMapping() *idtools.IdentityMapping { 83 return m.idmap 84} 85 86func getEmulator(p *pb.Platform, idmap *idtools.IdentityMapping) (*emulator, error) { 87 all := archutil.SupportedPlatforms(false) 88 m := make(map[string]struct{}, len(all)) 89 90 for _, p := range all { 91 m[p] = struct{}{} 92 } 93 94 pp := platforms.Normalize(specs.Platform{ 95 Architecture: p.Architecture, 96 OS: p.OS, 97 Variant: p.Variant, 98 }) 99 100 if _, ok := m[platforms.Format(pp)]; ok { 101 return nil, nil 102 } 103 104 a, ok := qemuArchMap[pp.Architecture] 105 if !ok { 106 a = pp.Architecture 107 } 108 109 fn, err := exec.LookPath("buildkit-qemu-" + a) 110 if err != nil { 111 return nil, errors.Errorf("no emulator available for %v", pp.OS) 112 } 113 114 return &emulator{path: fn}, nil 115} 116