Skip to content

Commit

Permalink
fix: report build error early while building recipe
Browse files Browse the repository at this point in the history
Fixes #839

Signed-off-by: Jeff MAURY <[email protected]>
  • Loading branch information
jeffmaury committed Apr 22, 2024
1 parent 17f214b commit 1cf3c6a
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 40 deletions.
80 changes: 80 additions & 0 deletions packages/backend/src/managers/applicationManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ describe('pullApplication', () => {
labels: {
[LABEL_RECIPE_ID]: 'recipe1',
},
abortController: expect.anything(),
},
);
expect(mocks.logUsageMock).toHaveBeenNthCalledWith(1, 'recipe.pull', {
Expand All @@ -326,6 +327,85 @@ describe('pullApplication', () => {
durationSeconds: 99,
});
});

test('pullApplication should clone repository and call downloadModelMain and fail on buildImage', async () => {
mockForPullApplication({
recipeFolderExists: false,
});
mocks.buildImageMock.mockRejectedValue(new Error('Build failed'));
mocks.listPodsMock.mockResolvedValue([]);
vi.spyOn(podman, 'isQEMUMachine').mockResolvedValue(false);
vi.spyOn(modelsManager, 'isModelOnDisk').mockReturnValue(false);
vi.spyOn(modelsManager, 'uploadModelToPodmanMachine').mockResolvedValue('path');
mocks.performDownloadMock.mockResolvedValue('path');
const recipe: Recipe = {
id: 'recipe1',
name: 'Recipe 1',
categories: [],
description: '',
ref: '000000',
readme: '',
repository: 'repo',
};
const model: ModelInfo = {
id: 'model1',
description: '',
hw: '',
license: '',
name: 'Model 1',
registry: '',
url: '',
memory: 1000,
};
mocks.inspectContainerMock.mockResolvedValue({
State: {
Running: true,
},
});
vi.spyOn(utils, 'getDurationSecondsSince').mockReturnValue(99);
let error: unknown = undefined;
try {
await manager.pullApplication(recipe, model);
} catch (err: unknown) {
error = err;
}
expect(error).toBeDefined();
const gitCloneOptions = {
repository: 'repo',
ref: '000000',
targetDirectory: '\\home\\user\\aistudio\\recipe1',
};
if (process.platform === 'win32') {
expect(processCheckoutMock).toHaveBeenNthCalledWith(1, gitCloneOptions);
} else {
gitCloneOptions.targetDirectory = '/home/user/aistudio/recipe1';
expect(processCheckoutMock).toHaveBeenNthCalledWith(1, gitCloneOptions);
}
expect(mocks.performDownloadMock).toHaveBeenCalledOnce();
expect(mocks.buildImageMock).toHaveBeenCalledOnce();
expect(mocks.buildImageMock).toHaveBeenCalledWith(
`${gitCloneOptions.targetDirectory}${path.sep}contextdir1`,
expect.anything(),
{
containerFile: 'Containerfile',
tag: 'recipe1-container1:latest',
labels: {
[LABEL_RECIPE_ID]: 'recipe1',
},
abortController: expect.anything(),
},
);
expect(mocks.logErrorMock).toHaveBeenNthCalledWith(
1,
'recipe.pull',
expect.objectContaining({
'recipe.id': 'recipe1',
'recipe.name': 'Recipe 1',
durationSeconds: 99,
message: 'error pulling application',
}),
);
});
test('pullApplication should not download model if already on disk', async () => {
mockForPullApplication({
recipeFolderExists: true,
Expand Down
94 changes: 54 additions & 40 deletions packages/backend/src/managers/applicationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,50 +409,64 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
const imageInfoList: ImageInfo[] = [];

// Promise all the build images
await Promise.all(
containers.map(container => {
const task = containerTasks[container.name];
const abortController = new AbortController();
try {
await Promise.all(
containers.map(container => {
const task = containerTasks[container.name];

// We use the parent directory of our configFile as the rootdir, then we append the contextDir provided
const context = path.join(getParentDirectory(configPath), container.contextdir);
console.log(`Application Manager using context ${context} for container ${container.name}`);
// We use the parent directory of our configFile as the rootdir, then we append the contextDir provided
const context = path.join(getParentDirectory(configPath), container.contextdir);
console.log(`Application Manager using context ${context} for container ${container.name}`);

// Ensure the context provided exist otherwise throw an Error
if (!fs.existsSync(context)) {
task.error = 'The context provided does not exist.';
this.taskRegistry.updateTask(task);
throw new Error('Context configured does not exist.');
}
// Ensure the context provided exist otherwise throw an Error
if (!fs.existsSync(context)) {
task.error = 'The context provided does not exist.';
this.taskRegistry.updateTask(task);
throw new Error('Context configured does not exist.');
}

const imageTag = this.getImageTag(recipe, container);
const buildOptions = {
containerFile: container.containerfile,
tag: imageTag,
labels: {
[LABEL_RECIPE_ID]: labels !== undefined && 'recipe-id' in labels ? labels['recipe-id'] : undefined,
},
};

return containerEngine
.buildImage(
context,
(event, data) => {
// todo: do something with the event
if (event === 'error' || (event === 'finish' && data !== '')) {
console.error('Something went wrong while building the image: ', data);
task.error = `Something went wrong while building the image: ${data}`;
this.taskRegistry.updateTask(task);
}
const imageTag = this.getImageTag(recipe, container);
const buildOptions = {
containerFile: container.containerfile,
tag: imageTag,
labels: {
[LABEL_RECIPE_ID]: labels !== undefined && 'recipe-id' in labels ? labels['recipe-id'] : undefined,
},
buildOptions,
)
.catch((err: unknown) => {
task.error = `Something went wrong while building the image: ${String(err)}`;
this.taskRegistry.updateTask(task);
throw new Error(`Something went wrong while building the image: ${String(err)}`);
});
}),
);
abortController: abortController,
};

let error = false;
return containerEngine
.buildImage(
context,
(event, data) => {
// todo: do something with the event
if (event === 'error' || (event === 'finish' && data !== '')) {
console.error('Something went wrong while building the image: ', data);
task.error = `Something went wrong while building the image: ${data}`;
this.taskRegistry.updateTask(task);
error = true;
}
},
buildOptions,
)
.catch((err: unknown) => {
task.error = `Something went wrong while building the image: ${String(err)}`;
this.taskRegistry.updateTask(task);
throw new Error(`Something went wrong while building the image: ${String(err)}`);
})
.then(() => {
if (error) {
throw new Error(`Something went wrong while building the image: ${imageTag}`);
}
});
}),
);
} catch (err: unknown) {
abortController.abort();
throw err;
}

// after image are built we return their data
const images = await containerEngine.listImages();
Expand Down

0 comments on commit 1cf3c6a

Please sign in to comment.