Skip to content
Merged
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
76 changes: 55 additions & 21 deletions lib/Lens.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,33 +110,46 @@ class Lens extends Configurable {
}
}

async buildSpecForContainer (inputTree, config) {
const { container: containerQuery } = config;

// check if image exists locally first
let imageHash;
async getImageHash (containerQuery) {
// Try local first (fastest)
try {
const inspectOutput = await Studio.execDocker(['inspect', containerQuery]);
const imageInfo = JSON.parse(inspectOutput)[0];
imageHash = imageInfo.Id;
logger.info(`found local image: ${containerQuery}@${imageHash}`);
logger.info(`found local image: ${containerQuery}@${imageInfo.Id}`);
return { hash: imageInfo.Id, isLocal: true };
} catch (err) {
// image doesn't exist locally or can't be inspected, try pulling
logger.info(`pulling image: ${containerQuery}`);

try {
await Studio.execDocker(['pull', containerQuery], { $relayStdout: true });
const inspectOutput = await Studio.execDocker(['inspect', containerQuery]);
const imageInfo = JSON.parse(inspectOutput)[0];
imageHash = imageInfo.Id;
} catch (err) {
throw new Error(`failed to pull container image ${containerQuery}: ${err.message}`);
}
// Not found locally, try remote manifest
}

if (!imageHash) {
throw new Error(`failed to get hash for container image ${containerQuery}`);
// Try remote manifest (doesn't pull the image)
try {
logger.info(`querying remote manifest for: ${containerQuery}`);
const manifestOutput = await Studio.execDocker(['manifest', 'inspect', containerQuery]);
const manifest = JSON.parse(manifestOutput);

// Extract digest from manifest
// For multi-arch images, manifest.config.digest contains the config digest
// For single-arch images, we may need to look at the digest field
const digest = manifest.config?.digest || manifest.digest;

if (!digest) {
throw new Error('No digest found in manifest');
}

// Ensure the digest is in the format sha256:...
const imageHash = digest.startsWith('sha256:') ? digest : `sha256:${digest}`;
logger.info(`found remote image hash: ${containerQuery}@${imageHash}`);
return { hash: imageHash, isLocal: false };
} catch (err) {
throw new Error(`failed to get hash for container image ${containerQuery}: ${err.message}`);
}
}

async buildSpecForContainer (inputTree, config) {
const { container: containerQuery } = config;

// Get image hash without pulling
const { hash: imageHash, isLocal } = await this.getImageHash(containerQuery);

// build spec
const data = {
Expand All @@ -152,7 +165,8 @@ class Lens extends Configurable {
return {
...await SpecObject.write(this.workspace.getRepo(), 'lens', data),
data,
type: 'container'
type: 'container',
imageIsLocal: isLocal
};
}

Expand Down Expand Up @@ -317,6 +331,26 @@ class Lens extends Configurable {
}
const [, sha256Hash] = containerMatch;

// Ensure image is available locally
try {
await Studio.execDocker(['inspect', sha256Hash]);
} catch (err) {
// Image not found locally, need to pull it
// Extract the original container query from spec to pull by name:tag
const containerQueryMatch = spec.container.match(/^(.+)@sha256:[a-f0-9]{64}$/);
if (containerQueryMatch) {
const containerQuery = containerQueryMatch[1];
logger.info(`pulling required image: ${containerQuery}`);
try {
await Studio.execDocker(['pull', containerQuery], { $relayStdout: true });
} catch (pullErr) {
throw new Error(`failed to pull container image ${containerQuery}: ${pullErr.message}`);
}
} else {
throw new Error(`cannot extract container query from spec.container: ${spec.container}`);
}
}

// create and start container
const persistentDebugContainer = process.env.HOLO_DEBUG_PERSIST_CONTAINER;
let containerId;
Expand Down
Loading