Skip to content

Commit

Permalink
feat: add build config configurator
Browse files Browse the repository at this point in the history
### What does this PR do?

* Adds automated way to create a build config json file
* Added to build page

### Screenshot / video of UI

<!-- If this PR is changing UI, please include
screenshots or screencasts showing the difference -->

### What issues does this PR fix or reference?

<!-- Include any related issues from Podman Desktop
repository (or from another issue tracker). -->

Closes #827

### How to test this PR?

<!-- Please explain steps to reproduce -->

Use build config and configure / create your own custom build config
   with users, filesystem, etc.
  • Loading branch information
cdrage committed Nov 18, 2024
1 parent 00113e6 commit 56ffd07
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 15 deletions.
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-credentials
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-credentials
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');
}
});
43 changes: 42 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,27 @@ 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) {
// Create a temporary path to store the buildConfig JSON
// with a temporary name
// eslint-disable-next-line sonarjs/pseudo-random
const buildConfigPath = path.join(os.tmpdir(), `${Math.floor(Math.random() * 100000)}.json`);

// Write the buildConfig JSON to the temporary file with JSON
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 +494,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

0 comments on commit 56ffd07

Please sign in to comment.