Skip to content

Commit 9b8aded

Browse files
authored
Merge pull request #6007 from tonistiigi/fix-platform-split-true
local: fix platform-split=true option
2 parents 5606f26 + db03322 commit 9b8aded

File tree

6 files changed

+117
-26
lines changed

6 files changed

+117
-26
lines changed

client/client_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
227227
testSnapshotWithMultipleBlobs,
228228
testExportLocalNoPlatformSplit,
229229
testExportLocalNoPlatformSplitOverwrite,
230+
testExportLocalForcePlatformSplit,
230231
testSolverOptLocalDirsStillWorks,
231232
testOCIIndexMediatype,
232233
testLayerLimitOnMounts,
@@ -6951,6 +6952,62 @@ func testExportLocalNoPlatformSplitOverwrite(t *testing.T, sb integration.Sandbo
69516952
},
69526953
}, "", frontend, nil)
69536954
require.Error(t, err)
6955+
require.ErrorContains(t, err, "cannot overwrite hello-linux from")
6956+
require.ErrorContains(t, err, "when split option is disabled")
6957+
}
6958+
6959+
func testExportLocalForcePlatformSplit(t *testing.T, sb integration.Sandbox) {
6960+
workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter, workers.FeatureMultiPlatform)
6961+
c, err := New(sb.Context(), sb.Address())
6962+
require.NoError(t, err)
6963+
defer c.Close()
6964+
6965+
frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
6966+
st := llb.Scratch().File(
6967+
llb.Mkfile("foo", 0600, []byte("hello")),
6968+
)
6969+
6970+
def, err := st.Marshal(ctx)
6971+
if err != nil {
6972+
return nil, err
6973+
}
6974+
6975+
return c.Solve(ctx, gateway.SolveRequest{
6976+
Definition: def.ToPB(),
6977+
})
6978+
}
6979+
6980+
destDir := t.TempDir()
6981+
_, err = c.Build(sb.Context(), SolveOpt{
6982+
Exports: []ExportEntry{
6983+
{
6984+
Type: ExporterLocal,
6985+
OutputDir: destDir,
6986+
Attrs: map[string]string{
6987+
"platform-split": "true",
6988+
},
6989+
},
6990+
},
6991+
}, "", frontend, nil)
6992+
require.NoError(t, err)
6993+
6994+
fis, err := os.ReadDir(destDir)
6995+
require.NoError(t, err)
6996+
6997+
require.Len(t, fis, 1, "expected one files in the output directory")
6998+
6999+
expPlatform := strings.ReplaceAll(platforms.FormatAll(platforms.DefaultSpec()), "/", "_")
7000+
_, err = os.Stat(filepath.Join(destDir, expPlatform+"/"))
7001+
require.NoError(t, err)
7002+
7003+
fis, err = os.ReadDir(filepath.Join(destDir, expPlatform))
7004+
require.NoError(t, err)
7005+
7006+
require.Len(t, fis, 1, "expected one files in the output directory for platform "+expPlatform)
7007+
7008+
dt, err := os.ReadFile(filepath.Join(destDir, expPlatform, "foo"))
7009+
require.NoError(t, err)
7010+
require.Equal(t, "hello", string(dt))
69547011
}
69557012

69567013
func readFileInImage(ctx context.Context, t *testing.T, c *Client, ref, path string) ([]byte, error) {

exporter/local/export.go

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,18 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source
117117

118118
export := func(ctx context.Context, k string, ref cache.ImmutableRef, attestations []exporter.Attestation) func() error {
119119
return func() error {
120-
outputFS, cleanup, err := CreateFS(ctx, sessionID, k, ref, attestations, now, e.opts)
120+
outputFS, cleanup, err := CreateFS(ctx, sessionID, k, ref, attestations, now, isMap, e.opts)
121121
if err != nil {
122122
return err
123123
}
124124
if cleanup != nil {
125125
defer cleanup()
126126
}
127127

128-
if !e.opts.PlatformSplit {
128+
lbl := "copying files"
129+
if !e.opts.UsePlatformSplit(isMap) {
129130
// check for duplicate paths
130-
err = outputFS.Walk(ctx, "", func(p string, entry os.DirEntry, err error) error {
131+
err = fsWalk(ctx, outputFS, "", func(p string, entry os.DirEntry, err error) error {
131132
if entry.IsDir() {
132133
return nil
133134
}
@@ -145,23 +146,18 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source
145146
if err != nil {
146147
return err
147148
}
148-
}
149-
150-
lbl := "copying files"
151-
if isMap {
149+
} else {
152150
lbl += " " + k
153-
if e.opts.PlatformSplit {
154-
st := &fstypes.Stat{
155-
Mode: uint32(os.ModeDir | 0755),
156-
Path: strings.ReplaceAll(k, "/", "_"),
157-
}
158-
if e.opts.Epoch != nil {
159-
st.ModTime = e.opts.Epoch.UnixNano()
160-
}
161-
outputFS, err = fsutil.SubDirFS([]fsutil.Dir{{FS: outputFS, Stat: st}})
162-
if err != nil {
163-
return err
164-
}
151+
st := &fstypes.Stat{
152+
Mode: uint32(os.ModeDir | 0755),
153+
Path: strings.ReplaceAll(k, "/", "_"),
154+
}
155+
if e.opts.Epoch != nil {
156+
st.ModTime = e.opts.Epoch.UnixNano()
157+
}
158+
outputFS, err = fsutil.SubDirFS([]fsutil.Dir{{FS: outputFS, Stat: st}})
159+
if err != nil {
160+
return err
165161
}
166162
}
167163

exporter/local/export_unix.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build !windows
2+
3+
package local
4+
5+
import (
6+
"context"
7+
gofs "io/fs"
8+
9+
"github.com/tonistiigi/fsutil"
10+
)
11+
12+
func fsWalk(ctx context.Context, fs fsutil.FS, s string, walkFn gofs.WalkDirFunc) error {
13+
return fs.Walk(ctx, s, walkFn)
14+
}

exporter/local/export_windows.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package local
2+
3+
import (
4+
"context"
5+
gofs "io/fs"
6+
7+
"github.com/Microsoft/go-winio"
8+
"github.com/tonistiigi/fsutil"
9+
)
10+
11+
func fsWalk(ctx context.Context, fs fsutil.FS, s string, walkFn gofs.WalkDirFunc) error {
12+
// Windows has some special files that require
13+
// SeBackupPrivilege to be accessed. Ref #4994
14+
return winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
15+
return fs.Walk(ctx, s, walkFn)
16+
})
17+
}

exporter/local/fs.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,18 @@ const (
3838
type CreateFSOpts struct {
3939
Epoch *time.Time
4040
AttestationPrefix string
41-
PlatformSplit bool
41+
PlatformSplit *bool
42+
}
43+
44+
func (c *CreateFSOpts) UsePlatformSplit(isMap bool) bool {
45+
if c.PlatformSplit == nil {
46+
return isMap
47+
}
48+
return *c.PlatformSplit
4249
}
4350

4451
func (c *CreateFSOpts) Load(opt map[string]string) (map[string]string, error) {
4552
rest := make(map[string]string)
46-
c.PlatformSplit = true
4753

4854
var err error
4955
c.Epoch, opt, err = epoch.ParseExporterAttrs(opt)
@@ -60,7 +66,7 @@ func (c *CreateFSOpts) Load(opt map[string]string) (map[string]string, error) {
6066
if err != nil {
6167
return nil, errors.Wrapf(err, "non-bool value for %s: %s", keyPlatformSplit, v)
6268
}
63-
c.PlatformSplit = b
69+
c.PlatformSplit = &b
6470
default:
6571
rest[k] = v
6672
}
@@ -69,7 +75,7 @@ func (c *CreateFSOpts) Load(opt map[string]string) (map[string]string, error) {
6975
return rest, nil
7076
}
7177

72-
func CreateFS(ctx context.Context, sessionID string, k string, ref cache.ImmutableRef, attestations []exporter.Attestation, defaultTime time.Time, opt CreateFSOpts) (fsutil.FS, func() error, error) {
78+
func CreateFS(ctx context.Context, sessionID string, k string, ref cache.ImmutableRef, attestations []exporter.Attestation, defaultTime time.Time, isMap bool, opt CreateFSOpts) (fsutil.FS, func() error, error) {
7379
var cleanup func() error
7480
var src string
7581
var err error
@@ -174,6 +180,7 @@ func CreateFS(ctx context.Context, sessionID string, k string, ref cache.Immutab
174180
return nil, nil, err
175181
}
176182
stmtFS := staticfs.NewFS()
183+
split := opt.UsePlatformSplit(isMap)
177184

178185
names := map[string]struct{}{}
179186
for i, stmt := range stmts {
@@ -183,7 +190,7 @@ func CreateFS(ctx context.Context, sessionID string, k string, ref cache.Immutab
183190
}
184191

185192
name := opt.AttestationPrefix + path.Base(attestations[i].Path)
186-
if !opt.PlatformSplit {
193+
if !split {
187194
nameExt := path.Ext(name)
188195
namBase := strings.TrimSuffix(name, nameExt)
189196
name = fmt.Sprintf("%s.%s%s", namBase, strings.ReplaceAll(k, "/", "_"), nameExt)

exporter/tar/export.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source
9595
}
9696

9797
now := time.Now().Truncate(time.Second)
98+
isMap := len(inp.Refs) > 0
9899

99100
getDir := func(ctx context.Context, k string, ref cache.ImmutableRef, attestations []exporter.Attestation) (*fsutil.Dir, error) {
100-
outputFS, cleanup, err := local.CreateFS(ctx, sessionID, k, ref, attestations, now, e.opts)
101+
outputFS, cleanup, err := local.CreateFS(ctx, sessionID, k, ref, attestations, now, isMap, e.opts)
101102
if err != nil {
102103
return nil, err
103104
}
@@ -119,7 +120,6 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source
119120
}, nil
120121
}
121122

122-
isMap := len(inp.Refs) > 0
123123
if _, ok := inp.Metadata[exptypes.ExporterPlatformsKey]; isMap && !ok {
124124
return nil, nil, errors.Errorf("unable to export multiple refs, missing platforms mapping")
125125
}

0 commit comments

Comments
 (0)