Skip to content

Commit c7edd6c

Browse files
tonistiigicrazy-max
authored andcommitted
client: add testExportLocalModeMultiPlatformKeepsAllPlatforms test
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
1 parent 3447449 commit c7edd6c

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

client/client_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
240240
testExportLocalForcePlatformSplit,
241241
testExportLocalModeCopyKeepsStaleDestinationFiles,
242242
testExportLocalModeDeleteRemovesStaleDestinationFiles,
243+
testExportLocalModeCopyMultiPlatformKeepsAllPlatforms,
244+
testExportLocalModeDeleteMultiPlatformKeepsAllPlatforms,
243245
testExportLocalModeInvalid,
244246
testSolverOptLocalDirsStillWorks,
245247
testOCIIndexMediatype,
@@ -7706,6 +7708,132 @@ func testExportLocalModeDeleteRemovesStaleDestinationFiles(t *testing.T, sb inte
77067708
require.ErrorIs(t, err, os.ErrNotExist)
77077709
}
77087710

7711+
func testExportLocalModeCopyMultiPlatformKeepsAllPlatforms(t *testing.T, sb integration.Sandbox) {
7712+
testExportLocalModeMultiPlatformKeepsAllPlatforms(t, sb, false)
7713+
}
7714+
7715+
func testExportLocalModeDeleteMultiPlatformKeepsAllPlatforms(t *testing.T, sb integration.Sandbox) {
7716+
testExportLocalModeMultiPlatformKeepsAllPlatforms(t, sb, true)
7717+
}
7718+
7719+
func testExportLocalModeMultiPlatformKeepsAllPlatforms(t *testing.T, sb integration.Sandbox, deleteMode bool) {
7720+
workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter, workers.FeatureMultiPlatform)
7721+
c, err := New(sb.Context(), sb.Address())
7722+
require.NoError(t, err)
7723+
defer c.Close()
7724+
7725+
const filesPerPlatform = 20
7726+
platformsToTest := []string{"linux/amd64", "linux/arm64", "linux/arm/v7", "linux/s390x"}
7727+
7728+
frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
7729+
res := gateway.NewResult()
7730+
expPlatforms := &exptypes.Platforms{
7731+
Platforms: make([]exptypes.Platform, len(platformsToTest)),
7732+
}
7733+
for i, platform := range platformsToTest {
7734+
st := llb.Scratch()
7735+
for j := range filesPerPlatform {
7736+
st = st.File(
7737+
llb.Mkfile(fmt.Sprintf("file-%03d.txt", j), 0600, fmt.Appendf(nil, "%s-%d", platform, j)),
7738+
)
7739+
}
7740+
7741+
def, err := st.Marshal(ctx)
7742+
if err != nil {
7743+
return nil, err
7744+
}
7745+
7746+
r, err := c.Solve(ctx, gateway.SolveRequest{
7747+
Definition: def.ToPB(),
7748+
})
7749+
if err != nil {
7750+
return nil, err
7751+
}
7752+
7753+
ref, err := r.SingleRef()
7754+
if err != nil {
7755+
return nil, err
7756+
}
7757+
7758+
_, err = ref.ToState()
7759+
if err != nil {
7760+
return nil, err
7761+
}
7762+
res.AddRef(platform, ref)
7763+
7764+
expPlatforms.Platforms[i] = exptypes.Platform{
7765+
ID: platform,
7766+
Platform: platforms.MustParse(platform),
7767+
}
7768+
}
7769+
dt, err := json.Marshal(expPlatforms)
7770+
if err != nil {
7771+
return nil, err
7772+
}
7773+
res.AddMeta(exptypes.ExporterPlatformsKey, dt)
7774+
7775+
return res, nil
7776+
}
7777+
7778+
destDir := t.TempDir()
7779+
7780+
// Pre-populate dest with directories matching the platform-split naming
7781+
// convention. This is critical for exposing the race in delete mode:
7782+
// each platform's fsutil.Receive does a dest-walk at the start and, with
7783+
// Merge=false (mode=delete), flags everything not in its own stream for
7784+
// deletion — including other platforms' pre-existing directories. Without
7785+
// pre-population the dest starts empty and the concurrent dest-walks all
7786+
// see nothing to delete, hiding the cross-platform deletion race.
7787+
err = os.WriteFile(filepath.Join(destDir, "stale.txt"), []byte("stale"), 0600)
7788+
require.NoError(t, err)
7789+
for _, platform := range platformsToTest {
7790+
platDir := filepath.Join(destDir, strings.ReplaceAll(platform, "/", "_"))
7791+
err = os.MkdirAll(platDir, 0755)
7792+
require.NoError(t, err)
7793+
for j := range filesPerPlatform {
7794+
err = os.WriteFile(filepath.Join(platDir, fmt.Sprintf("old-%03d.txt", j)), []byte("old"), 0600)
7795+
require.NoError(t, err)
7796+
}
7797+
}
7798+
7799+
attrs := map[string]string{}
7800+
if deleteMode {
7801+
attrs["mode"] = "delete"
7802+
}
7803+
7804+
_, err = c.Build(sb.Context(), SolveOpt{
7805+
Exports: []ExportEntry{
7806+
{
7807+
Type: ExporterLocal,
7808+
OutputDir: destDir,
7809+
Attrs: attrs,
7810+
},
7811+
},
7812+
}, "", frontend, nil)
7813+
require.NoError(t, err)
7814+
7815+
if deleteMode {
7816+
// Stale top-level file must be gone.
7817+
_, err = os.Stat(filepath.Join(destDir, "stale.txt"))
7818+
require.ErrorIs(t, err, os.ErrNotExist)
7819+
}
7820+
7821+
// Every platform's build output must survive (no cross-platform deletion).
7822+
for _, platform := range platformsToTest {
7823+
platformDir := filepath.Join(destDir, strings.ReplaceAll(platform, "/", "_"))
7824+
for j := range filesPerPlatform {
7825+
dt, err := os.ReadFile(filepath.Join(platformDir, fmt.Sprintf("file-%03d.txt", j)))
7826+
require.NoError(t, err, "missing build output file-%03d.txt for %s", j, platform)
7827+
require.Equal(t, fmt.Sprintf("%s-%d", platform, j), string(dt))
7828+
}
7829+
if deleteMode {
7830+
// Pre-existing files within each platform dir must be cleaned up.
7831+
_, err = os.Stat(filepath.Join(platformDir, "old-000.txt"))
7832+
require.ErrorIs(t, err, os.ErrNotExist, "stale file not removed for %s", platform)
7833+
}
7834+
}
7835+
}
7836+
77097837
func testExportLocalModeInvalid(t *testing.T, sb integration.Sandbox) {
77107838
c, err := New(sb.Context(), sb.Address())
77117839
require.NoError(t, err)

0 commit comments

Comments
 (0)