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

feat: add build config configurator #1026

Merged
merged 2 commits into from
Dec 19, 2024
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
93 changes: 92 additions & 1 deletion packages/backend/src/build-disk-image.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ import {
createPodmanCLIRunCommand,
getBuilder,
getUnusedName,
createBuildConfigJSON,
} from './build-disk-image';
import { bootcImageBuilderCentos, bootcImageBuilderRHEL } from './constants';
import type { ContainerInfo, Configuration } from '@podman-desktop/api';
import { containerEngine } from '@podman-desktop/api';
import type { BootcBuildInfo } from '/@shared/src/models/bootc';
import type { BootcBuildInfo, BuildConfig } from '/@shared/src/models/bootc';
import * as fs from 'node:fs';
import path, { resolve } from 'node:path';

Expand Down Expand Up @@ -470,3 +471,93 @@ test('test chown works when passed into createBuilderImageOptions', async () =>
expect(options.Cmd).toContain('--chown');
expect(options.Cmd).toContain(build.chown);
});

test('test createBuildConfigJSON function works when passing in a build config with user, filesystem and kernel', async () => {
const buildConfig = {
user: [
{
name: 'test-user',
// eslint-disable-next-line sonarjs/no-hardcoded-passwords
password: 'test-password',
key: 'test-key',
groups: ['test-group'],
},
],
filesystem: [
{
mountpoint: '/test/mountpoint',
minsize: '1GB',
},
],
kernel: {
append: 'test-append',
},
} as BuildConfig;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const buildConfigJson: Record<string, any> = createBuildConfigJSON(buildConfig);
expect(buildConfigJson).toBeDefined();

// buildConfigJson is Record<string, unknown>, but check that the first one is 'customnizations'
const keys = Object.keys(buildConfigJson);
expect(keys[0]).toEqual('customizations');

// Check that user, filesystem and kernel are included in the JSON
expect(buildConfigJson.customizations).toBeDefined();
expect(buildConfigJson?.customizations?.user[0]).toBeDefined();
expect(buildConfigJson?.customizations?.filesystem[0]).toBeDefined();
expect(buildConfigJson?.customizations?.kernel).toBeDefined();
});

test('test building with a buildConfig JSON file that a temporary file for buildconfig is passed to binds', async () => {
const buildConfig = {
user: [
{
name: 'test-user',
// eslint-disable-next-line sonarjs/no-hardcoded-passwords
password: 'test-password',
key: 'test-key',
groups: ['test-group'],
},
],
filesystem: [
{
mountpoint: '/test/mountpoint',
minsize: '1GB',
},
],
kernel: {
append: 'test-append',
},
} as BuildConfig;

const name = 'test123-bootc-image-builder';
const build = {
image: 'test-image',
tag: 'latest',
type: ['raw'],
arch: 'amd64',
folder: '/foo/bar/qemutest4',
buildConfig: buildConfig,
} as BootcBuildInfo;

// Spy on fs.writeFileSync to make sure it is called
vi.mock('node:fs');
vi.spyOn(fs, 'writeFileSync');

const options = createBuilderImageOptions(name, build);

// Expect writeFileSync was called
expect(fs.writeFileSync).toHaveBeenCalled();

// Expect that options.HostConfig.Binds includes a buildconfig file
expect(options).toBeDefined();
expect(options.HostConfig).toBeDefined();
expect(options.HostConfig?.Binds).toBeDefined();
if (options.HostConfig?.Binds) {
expect(options.HostConfig.Binds.length).toEqual(3);
expect(options.HostConfig.Binds[0]).toEqual(build.folder + ':/output/');
expect(options.HostConfig.Binds[1]).toEqual('/var/lib/containers/storage:/var/lib/containers/storage');
expect(options.HostConfig.Binds[2]).toContain('config.json:ro');
}
});
41 changes: 40 additions & 1 deletion packages/backend/src/build-disk-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import path, { resolve } from 'node:path';
import os from 'node:os';
import * as containerUtils from './container-utils';
import { bootcImageBuilder, bootcImageBuilderCentos, bootcImageBuilderRHEL } from './constants';
import type { BootcBuildInfo, BuildType } from '/@shared/src/models/bootc';
import type { BootcBuildInfo, BuildConfig, BuildType } from '/@shared/src/models/bootc';
import type { History } from './history';
import * as machineUtils from './machine-utils';
import { getConfigurationValue, telemetryLogger } from './extension';
Expand Down Expand Up @@ -465,6 +465,25 @@ export function createBuilderImageOptions(
}
}

// Check if build.buildConfig has ANYTHING defined, make sure it is not empty.
if (build.buildConfig) {
const buildConfig = createBuildConfigJSON(build.buildConfig);

// Make sure that cutomizations is exists and is not empty before adding it to the container.
if (buildConfig.customizations && Object.keys(buildConfig.customizations).length > 0) {
// Use the folder of the build to store the buildConfig JSON file as config.json
const buildConfigPath = path.join(build.folder, 'config.json');

// Write the buildConfig JSON to the file we'll be using
fs.writeFileSync(buildConfigPath, JSON.stringify(buildConfig, undefined, 2));

// Add the mount to the configuration file
if (options.HostConfig?.Binds) {
options.HostConfig.Binds.push(buildConfigPath + ':/config.json:ro');
}
}
}

// If there is the chown in build, add the --chown flag to the command with the value in chown
if (build.chown) {
cmd.push('--chown', build.chown);
Expand All @@ -473,6 +492,26 @@ export function createBuilderImageOptions(
return options;
}

// Function that takes in BuildConfig and creates a JSON object out of the contents.
// We will then return it as "cutomizations" which is required by bootc-image-builder
export function createBuildConfigJSON(buildConfig: BuildConfig): Record<string, unknown> {
const config: Record<string, unknown> = {};

if (buildConfig.user && buildConfig.user.length > 0) {
config.user = buildConfig.user;
}

if (buildConfig.filesystem && buildConfig.filesystem.length > 0) {
config.filesystem = buildConfig.filesystem;
}

if (buildConfig.kernel?.append) {
config.kernel = buildConfig.kernel;
}

return { customizations: config };
}

// Creates a command that will be used to build the image on Linux. This includes adding the transfer-to-root script as well as the actual build command.
// we also export to the log file during this process too.
export function linuxBuildCommand(
Expand Down
Loading