Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update direct-tarball to follow spec + refactor processFeatureIdentifier tests #105

Merged
merged 21 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/spec-configuration/containerFeaturesConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,16 +545,26 @@ export async function processFeatureIdentifier(output: Log, env: NodeJS.ProcessE
// remote tar file
if (type === 'direct-tarball') {
output.write(`Remote tar file found.`);
let input = userFeature.id.replace(/\/+$/, '');
const featureIdDelimiter = input.lastIndexOf('#');
const id = input.substring(featureIdDelimiter + 1);
const tarballUri = new URL.URL(userFeature.id);

const fullPath = tarballUri.pathname;
const tarballName = fullPath.substring(fullPath.lastIndexOf('/') + 1);
output.write(`tarballName = ${tarballName}`, LogLevel.Trace);

const regex = new RegExp('devcontainer-feature-(.*).tgz');
const matches = regex.exec(tarballName);

if (!matches || matches.length !== 2) {
output.write(`Expected tarball name to follow 'devcontainer-feature-<feature-id>.tgz' format. Received '${tarballName}'`, LogLevel.Error);
return undefined;
}
const id = matches[1];

if (id === '' || !allowedFeatureIdRegex.test(id)) {
output.write(`Parse error. Specify a feature id with alphanumeric, dash, or underscore characters. Provided: ${id}.`, LogLevel.Error);
output.write(`Parse error. Specify a feature id with alphanumeric, dash, or underscore characters. Received ${id}.`, LogLevel.Error);
return undefined;
}

const tarballUri = new URL.URL(input.substring(0, featureIdDelimiter)).toString();
let feat: Feature = {
id: id,
name: userFeature.id,
Expand All @@ -565,7 +575,7 @@ export async function processFeatureIdentifier(output: Log, env: NodeJS.ProcessE
let newFeaturesSet: FeatureSet = {
sourceInformation: {
type: 'direct-tarball',
tarballUri: tarballUri
tarballUri: tarballUri.toString()
},
features: [feat],
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:16",
"features": {
"https://github.com/codspace/features/releases/download/tarball02/devcontainer-feature-docker-in-docker.tgz": {}
}
}
11 changes: 5 additions & 6 deletions src/test/container-features/containerFeaturesOCI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { createPlainLog, LogLevel, makeLog } from '../../spec-utils/log';
export const output = makeLog(createPlainLog(text => process.stdout.write(text), () => LogLevel.Trace));

describe('Test OCI Pull', () => {

it('Parse OCI identifier', async () => {
const feat = getFeatureRef(output, 'ghcr.io/codspace/features/ruby:1');
output.write(`feat: ${JSON.stringify(feat)}`);
Expand All @@ -19,8 +18,8 @@ describe('Test OCI Pull', () => {
});

it('Get a manifest by tag', async () => {
const featureRef = getFeatureRef(output, 'ghcr.io/codspace/features/ruby:1.0.14');
const manifest = await getFeatureManifest(output, process.env, 'https://ghcr.io/v2/codspace/features/ruby/manifests/1.0.14', featureRef);
const featureRef = getFeatureRef(output, 'ghcr.io/codspace/features/ruby:1.0.13');
const manifest = await getFeatureManifest(output, process.env, 'https://ghcr.io/v2/codspace/features/ruby/manifests/1.0.13', featureRef);
assert.isNotNull(manifest);
assert.exists(manifest);

Expand All @@ -37,12 +36,12 @@ describe('Test OCI Pull', () => {
output.write(`Layer imageTitle: ${layer.annotations['org.opencontainers.image.title']}`);
});

assert.equal(manifest.layers[0].digest, 'sha256:c33008d0dc12d0e631734082401bec692da809eae2ac51e24f58c1cac68fc0c9');
assert.equal(manifest.layers[0].digest, 'sha256:8f59630bd1ba6d9e78b485233a0280530b3d0a44338f472206090412ffbd3efb');
});

it('Download a feature', async () => {
const featureRef = getFeatureRef(output, 'ghcr.io/codspace/features/ruby:1.0.14');
const result = await getFeatureBlob(output, process.env, 'https://ghcr.io/v2/codspace/features/ruby/blobs/sha256:c33008d0dc12d0e631734082401bec692da809eae2ac51e24f58c1cac68fc0c9', '/tmp', '/tmp/featureTest', featureRef);
const featureRef = getFeatureRef(output, 'ghcr.io/codspace/features/ruby:1.0.13');
const result = await getFeatureBlob(output, process.env, 'https://ghcr.io/v2/codspace/features/ruby/blobs/sha256:8f59630bd1ba6d9e78b485233a0280530b3d0a44338f472206090412ffbd3efb', '/tmp', '/tmp/featureTest', featureRef);
assert.isTrue(result);
});
});
19 changes: 18 additions & 1 deletion src/test/container-features/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ describe('Dev Container Features E2E (remote)', function () {
});
});


describe('v2 - Dockerfile feature Configs', () => {

describe(`dockerfile-with-v2-oci-features`, () => {
Expand All @@ -71,6 +70,24 @@ describe('Dev Container Features E2E (remote)', function () {
});
});
});

describe('v2 - Image property feature Configs', () => {

describe(`image-with-v2-tarball`, () => {
let containerId: string | null = null;
const testFolder = `${__dirname}/configs/image-with-v2-tarball`;
beforeEach(async () => containerId = (await devContainerUp(cli, testFolder, { 'logLevel': 'trace' })).containerId);
afterEach(async () => await devContainerDown({ containerId }));
it('should detect docker installed (--privileged flag implicitly passed)', async () => {
// NOTE: Doing a docker ps will ensure that the --privileged flag was set by the feature
const res = await shellExec(`${cli} exec --workspace-folder ${testFolder} docker ps`);
const response = JSON.parse(res.stdout);
console.log(res.stderr);
assert.equal(response.outcome, 'success');
assert.match(res.stderr, /CONTAINER ID/);
});
});
});
});


Expand Down
Loading