Skip to content

Commit e7850ed

Browse files
committed
add tests
1 parent 1455093 commit e7850ed

File tree

2 files changed

+109
-16
lines changed

2 files changed

+109
-16
lines changed

src/test/cli.build.test.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as assert from 'assert';
88
import * as path from 'path';
99
import * as os from 'os';
1010
import { buildKitOptions, shellExec } from './testUtils';
11-
import { ImageDetails } from '../spec-shutdown/dockerUtils';
11+
import { ImageDetails, ManifestDetail } from '../spec-shutdown/dockerUtils';
1212
import { envListToObj } from '../spec-node/utils';
1313

1414
const pkg = require('../../package.json');
@@ -433,5 +433,72 @@ describe('Dev Containers CLI', function () {
433433
const details = JSON.parse((await shellExec(`docker inspect ${response.imageName}`)).stdout)[0] as ImageDetails;
434434
assert.strictEqual(details.Config.Labels?.test_build_options, 'success');
435435
});
436+
437+
it(`should build successfully with platform args container builder`, async () => {
438+
const builderName = 'test-container-builder';
439+
const registryName = 'test-registry';
440+
const imageName = `localhost:5000/test:latest`;
441+
try {
442+
await shellExec(`docker run -d --name ${registryName} -p 5000:5000 registry`);
443+
const testFolder = `${__dirname}/configs/dockerfile-with-automatic-platform-args`;
444+
await shellExec(`docker buildx create --name ${builderName} --driver docker-container --use --driver-opt network=host --config ${testFolder}/config.toml`);
445+
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --log-level trace --platform linux/arm64,linux/amd64 --push --image-name ${imageName}`);
446+
console.log(res.stdout);
447+
const response = JSON.parse(res.stdout);
448+
assert.equal(response.outcome, 'success');
449+
const details = JSON.parse((await shellExec(`docker manifest inspect --insecure ${imageName}`)).stdout) as ManifestDetail;
450+
451+
const osSet = new Set(details.manifests.map(manifest => manifest.platform.os));
452+
assert.ok(osSet.has('linux'), 'Expected linux OS to be present');
453+
454+
const archSet = new Set(details.manifests.map(manifest => manifest.platform.architecture));
455+
assert.ok(archSet.has('arm64'), 'Expected linux/arm64 architecture to be present');
456+
assert.ok(archSet.has('amd64'), 'Expected linux/amd64 architecture to be present');
457+
458+
const amd64Manifest = details.manifests.find(manifest => manifest.platform.architecture === 'amd64');
459+
assert.ok(amd64Manifest, 'Expected linux/amd64 manifest to be present');
460+
461+
await shellExec(`docker pull ${imageName}@${amd64Manifest.digest}`);
462+
const amd64Details = JSON.parse((await shellExec(`docker inspect ${imageName}@${amd64Manifest.digest}`)).stdout)[0] as ImageDetails;
463+
assert.strictEqual(amd64Details.Config.Labels?.Architecture, 'amd64');
464+
assert.strictEqual(amd64Details.Config.Labels?.TargetPlatform, 'linux/amd64');
465+
assert.strictEqual(amd64Details.Config.Labels?.TargetOS, 'linux');
466+
assert.strictEqual(amd64Details.Config.Labels?.TargetArch, 'amd64');
467+
assert.strictEqual(amd64Details.Config.Labels?.TargetVariant, '');
468+
469+
const arm64Manifest = details.manifests.find(manifest => manifest.platform.architecture === 'arm64');
470+
assert.ok(arm64Manifest, 'Expected linux/arm64 manifest to be present');
471+
472+
await shellExec(`docker pull ${imageName}@${arm64Manifest.digest}`);
473+
const arm64Details = JSON.parse((await shellExec(`docker inspect ${imageName}@${arm64Manifest.digest}`)).stdout)[0] as ImageDetails;
474+
assert.strictEqual(arm64Details.Config.Labels?.Architecture, 'arm64');
475+
assert.strictEqual(arm64Details.Config.Labels?.TargetPlatform, 'linux/arm64');
476+
assert.strictEqual(arm64Details.Config.Labels?.TargetOS, 'linux');
477+
assert.strictEqual(arm64Details.Config.Labels?.TargetArch, 'arm64');
478+
assert.strictEqual(arm64Details.Config.Labels?.TargetVariant, '');
479+
480+
} finally {
481+
await shellExec(`docker rm -f ${registryName}`);
482+
await shellExec(`docker buildx rm ${builderName}`);
483+
}
484+
});
485+
it(`should fail with inconsistent base images`, async () => {
486+
const builderName = 'test-container-builder';
487+
try {
488+
await shellExec(`docker buildx create --name ${builderName} --driver docker-container --use`);
489+
const testFolder = `${__dirname}/configs/dockerfile-with-inconsistent-base-image`;
490+
const res = await shellExec(`${cli} build --workspace-folder ${testFolder} --log-level trace --platform linux/arm64,linux/amd64`);
491+
console.log(res.stdout);
492+
const response = JSON.parse(res.stdout);
493+
assert.equal(response.outcome, 'success');
494+
} catch (error) {
495+
assert.equal(error.error.code, 1, 'Should fail with exit code 1');
496+
const res = JSON.parse(error.stdout);
497+
assert.equal(res.outcome, 'error');
498+
assert.match(res.message, /Inconsistent base image used for multi-platform builds. Please check your Dockerfile./);
499+
} finally {
500+
await shellExec(`docker buildx rm ${builderName}`);
501+
}
502+
});
436503
});
437504
});

src/test/dockerfileUtils.test.ts

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ FROM ubuntu:latest as dev
178178
const info = await internalGetImageBuildInfoFromDockerfile(async (imageName) => {
179179
assert.strictEqual(imageName, 'ubuntu:latest');
180180
return details;
181-
}, dockerfile, {}, undefined, testSubstitute, nullLog, false);
181+
}, dockerfile, {}, undefined, testSubstitute, nullLog, false, { os: 'linux', arch: 'amd64' });
182182
assert.strictEqual(info.user, 'imageUser');
183183
assert.strictEqual(info.metadata.config.length, 1);
184184
assert.strictEqual(info.metadata.config[0].id, 'testid-substituted');
@@ -206,7 +206,7 @@ USER dockerfileUserB
206206
const info = await internalGetImageBuildInfoFromDockerfile(async (imageName) => {
207207
assert.strictEqual(imageName, 'ubuntu:latest');
208208
return details;
209-
}, dockerfile, {}, undefined, testSubstitute, nullLog, false);
209+
}, dockerfile, {}, undefined, testSubstitute, nullLog, false, { os: 'linux', arch: 'amd64' });
210210
assert.strictEqual(info.user, 'dockerfileUserB');
211211
assert.strictEqual(info.metadata.config.length, 0);
212212
assert.strictEqual(info.metadata.raw.length, 0);
@@ -220,7 +220,7 @@ describe('findBaseImage', () => {
220220
USER user1
221221
`;
222222
const extracted = extractDockerfile(dockerfile);
223-
const image = findBaseImage(extracted, {}, undefined);
223+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
224224
assert.strictEqual(image, 'image1');
225225
});
226226

@@ -231,7 +231,7 @@ ARG IMAGE_USER=user2
231231
USER $IMAGE_USER
232232
`;
233233
const extracted = extractDockerfile(dockerfile);
234-
const image = findBaseImage(extracted, {}, undefined);
234+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
235235
assert.strictEqual(image, 'image2');
236236
});
237237

@@ -244,7 +244,7 @@ USER $IMAGE_USER
244244
const extracted = extractDockerfile(dockerfile);
245245
const image = findBaseImage(extracted, {
246246
'BASE_IMAGE': 'image3'
247-
}, undefined);
247+
}, undefined, { os: 'linux', arch: 'amd64' });
248248
assert.strictEqual(image, 'image3');
249249
});
250250

@@ -256,7 +256,7 @@ FROM image3 as stage3
256256
FROM image4 as stage4
257257
`;
258258
const extracted = extractDockerfile(dockerfile);
259-
const image = findBaseImage(extracted, {}, 'stage2');
259+
const image = findBaseImage(extracted, {}, 'stage2', { os: 'linux', arch: 'amd64' });
260260
assert.strictEqual(image, 'image3');
261261
});
262262

@@ -268,7 +268,7 @@ FROM "\${BASE_IMAGE}"
268268
`;
269269
const extracted = extractDockerfile(dockerfile);
270270
assert.strictEqual(extracted.stages.length, 1);
271-
const image = findBaseImage(extracted, {}, undefined);
271+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
272272
assert.strictEqual(image, 'ubuntu:latest');
273273
});
274274

@@ -282,7 +282,7 @@ FROM \${cloud:+mcr.microsoft.com/}azure-cli:latest
282282
assert.strictEqual(extracted.stages.length, 1);
283283
const image = findBaseImage(extracted, {
284284
'cloud': 'true'
285-
}, undefined);
285+
}, undefined, { os: 'linux', arch: 'amd64' });
286286
assert.strictEqual(image, 'mcr.microsoft.com/azure-cli:latest');
287287
});
288288

@@ -293,7 +293,7 @@ FROM \${cloud:+mcr.microsoft.com/}azure-cli:latest
293293
`;
294294
const extracted = extractDockerfile(dockerfile);
295295
assert.strictEqual(extracted.stages.length, 1);
296-
const image = findBaseImage(extracted, {}, undefined);
296+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
297297
assert.strictEqual(image, 'azure-cli:latest');
298298
});
299299

@@ -306,7 +306,7 @@ FROM \${cloud:-mcr.microsoft.com/}azure-cli:latest
306306
assert.strictEqual(extracted.stages.length, 1);
307307
const image = findBaseImage(extracted, {
308308
'cloud': 'ghcr.io/'
309-
}, undefined);
309+
}, undefined, { os: 'linux', arch: 'amd64' });
310310
assert.strictEqual(image, 'ghcr.io/azure-cli:latest');
311311
});
312312

@@ -317,7 +317,7 @@ FROM \${cloud:-mcr.microsoft.com/}azure-cli:latest
317317
`;
318318
const extracted = extractDockerfile(dockerfile);
319319
assert.strictEqual(extracted.stages.length, 1);
320-
const image = findBaseImage(extracted, {}, undefined);
320+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
321321
assert.strictEqual(image, 'mcr.microsoft.com/azure-cli:latest');
322322
});
323323

@@ -334,7 +334,8 @@ FROM \${cloud:+"mcr.microsoft.com/"}azure-cli:latest"
334334
{
335335
cloud: 'true',
336336
},
337-
undefined
337+
undefined,
338+
{ os: 'linux', arch: 'amd64' }
338339
);
339340
assert.strictEqual(image, 'mcr.microsoft.com/azure-cli:latest');
340341
});
@@ -347,7 +348,7 @@ FROM "\${cloud:+"mcr.microsoft.com/"}azure-cli:latest"
347348

348349
const extracted = extractDockerfile(dockerfile);
349350
assert.strictEqual(extracted.stages.length, 1);
350-
const image = findBaseImage(extracted, {}, undefined);
351+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
351352
assert.strictEqual(image, 'azure-cli:latest');
352353
});
353354

@@ -364,7 +365,8 @@ FROM "\${cloud:-"mcr.microsoft.com/"}azure-cli:latest"
364365
{
365366
cloud: 'ghcr.io/',
366367
},
367-
undefined
368+
undefined,
369+
{ os: 'linux', arch: 'amd64' }
368370
);
369371
assert.strictEqual(image, 'ghcr.io/azure-cli:latest');
370372
});
@@ -377,9 +379,33 @@ FROM \${cloud:-"mcr.microsoft.com/"}azure-cli:latest as label
377379

378380
const extracted = extractDockerfile(dockerfile);
379381
assert.strictEqual(extracted.stages.length, 1);
380-
const image = findBaseImage(extracted, {}, undefined);
382+
const image = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
381383
assert.strictEqual(image, 'mcr.microsoft.com/azure-cli:latest');
382384
});
385+
386+
it('Multi-platform build', async () => {
387+
const dockerfile = `
388+
ARG TARGETARCH
389+
FROM image1 AS base-1
390+
391+
FROM image2 AS base-2
392+
393+
FROM --platform=amd64 base-1 AS amd64-base
394+
LABEL Architecture="amd64"
395+
396+
FROM --platform=arm64 base-2 AS arm64-base
397+
LABEL Architecture="arm64"
398+
399+
FROM \${TARGETARCH}-base AS final
400+
`;
401+
402+
const extracted = extractDockerfile(dockerfile);
403+
assert.strictEqual(extracted.stages.length, 5);
404+
const amd64BaseImage = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'amd64' });
405+
assert.strictEqual(amd64BaseImage, 'image1');
406+
const arm64BaseImage = findBaseImage(extracted, {}, undefined, { os: 'linux', arch: 'arm64' });
407+
assert.strictEqual(arm64BaseImage, 'image2');
408+
});
383409
});
384410
});
385411
});

0 commit comments

Comments
 (0)