Skip to content

Commit

Permalink
feat: add chown (linux only) (#695)
Browse files Browse the repository at this point in the history
* feat: add chown (linux only)

### What does this PR do?

* Adds chown to the advanced settings page for build
* Only applicable to Linux (see:
  osbuild/bootc-image-builder#576)

### 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 #472

### How to test this PR?

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

1. Be on Linux
2. Build an image
3. See that it is chown (ex. ls -l /outputdir)

Signed-off-by: Charlie Drage <[email protected]>

* by default fill in uid and gid

Signed-off-by: Charlie Drage <[email protected]>

---------

Signed-off-by: Charlie Drage <[email protected]>
  • Loading branch information
cdrage authored Aug 6, 2024
1 parent 7df2443 commit 357038a
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 1 deletion.
6 changes: 5 additions & 1 deletion packages/backend/src/api-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { History } from './history';
import * as containerUtils from './container-utils';
import { Messages } from '/@shared/src/messages/Messages';
import { telemetryLogger } from './extension';
import { checkPrereqs, isLinux } from './machine-utils';
import { checkPrereqs, isLinux, getUidGid } from './machine-utils';

export class BootcApiImpl implements BootcApi {
private history: History;
Expand Down Expand Up @@ -244,6 +244,10 @@ export class BootcApiImpl implements BootcApi {
return isLinux();
}

async getUidGid(): Promise<string> {
return getUidGid();
}

// The API does not allow callbacks through the RPC, so instead
// we send "notify" messages to the frontend to trigger a refresh
// this method is internal and meant to be used by the API implementation
Expand Down
25 changes: 25 additions & 0 deletions packages/backend/src/build-disk-image.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,28 @@ test('test build config json passed in', async () => {
expect(options.HostConfig.Binds[2]).toEqual(build.buildConfigFilePath + ':/config.json:ro');
}
});

test('test chown works when passed into createBuilderImageOptions', async () => {
const name = 'test123-bootc-image-builder';
const build = {
image: 'test-image',
tag: 'latest',
type: ['raw'],
arch: 'amd64',
folder: '/tmp/foo/bar/qemutest4',
chown: '1000:1000',
} as BootcBuildInfo;

const options = createBuilderImageOptions(name, build);

expect(options).toBeDefined();
expect(options.HostConfig).toBeDefined();
expect(options.HostConfig?.Binds).toBeDefined();
if (options.HostConfig?.Binds) {
expect(options.HostConfig.Binds.length).toEqual(2);
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.Cmd).toContain('--chown');
expect(options.Cmd).toContain(build.chown);
});
5 changes: 5 additions & 0 deletions packages/backend/src/build-disk-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,11 @@ export function createBuilderImageOptions(
}
}

// 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);
}

return options;
}

Expand Down
9 changes: 9 additions & 0 deletions packages/backend/src/machine-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,12 @@ const linux = os.platform() === 'linux';
export function isLinux(): boolean {
return linux;
}

// Get the GID and UID of the current user and return in the format gid:uid
// in order for this to work, we must get this information from process.exec
// since there is no native way via node
export async function getUidGid(): Promise<string> {
const { stdout: uidOutput } = await extensionApi.process.exec('id', ['-u']);
const { stdout: gidOutput } = await extensionApi.process.exec('id', ['-g']);
return `${uidOutput.trim()}:${gidOutput.trim()}`;
}
3 changes: 3 additions & 0 deletions packages/frontend/src/Build.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,4 +728,7 @@ test('collapse and uncollapse of advanced options', async () => {
// expect build config to be shown
const buildConfig2 = screen.queryByRole('label', { name: 'Build config' });
expect(buildConfig2).toBeDefined();
// Expect chown to be shown
const chown = screen.queryByRole('label', { name: 'Change file owner and group' });
expect(chown).toBeDefined();
});
37 changes: 37 additions & 0 deletions packages/frontend/src/Build.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ let availableArchitectures: string[] = [];
// Build options
let buildFolder: string;
let buildConfigFile: string;
let buildChown: string;
let buildType: BuildType[] = [];
let buildArch: string | undefined;
let buildFilesystem: string = ''; // Default filesystem auto-selected / empty
Expand Down Expand Up @@ -124,6 +125,17 @@ async function fillArchitectures(historyInfo: BootcBuildInfo[]) {
}
}
// This will fill the chown function by getting the user and group ID from the OS
// and filling in the information in the chown input field.
async function fillChownOption() {
try {
const gidUid = await bootcClient.getUidGid();
buildChown = gidUid;
} catch (error) {
console.error('Error getting UID and GID:', error);
}
}
async function validate() {
let prereqs = await bootcClient.checkPrereqs();
if (prereqs) {
Expand Down Expand Up @@ -193,6 +205,7 @@ async function buildBootcImage() {
type: buildType,
arch: buildArch,
filesystem: buildFilesystem,
chown: buildChown,
awsAmiName: awsAmiName,
awsBucket: awsBucket,
awsRegion: awsRegion,
Expand Down Expand Up @@ -291,6 +304,10 @@ onMount(async () => {
await fillBuildOptions(historyInfo);
await fillArchitectures(historyInfo);
if (isLinux) {
await fillChownOption();
}
validate();
});
Expand Down Expand Up @@ -689,6 +706,26 @@ export function goToHomePage(): void {
</p>
</div>

<!-- chown, this option is only available for Linux users -->
{#if isLinux}
<div class="mb-2">
<label for="chown" class="block mb-2 font-semibold">Change file owner and group</label>
<div class="flex flex-row space-x-3">
<Input
name="chown"
id="chown"
bind:value={buildChown}
placeholder="UID and GID parameters (ex. 1000:1000)"
class="w-full"
aria-label="chown-select" />
</div>
<p class="text-sm text-[var(--pd-content-text)] pt-2">
Linux only. By default the UID and GID of the current user is used. This option allows you to
change the owner and group of the files in the output directory.
</p>
</div>
{/if}

<!-- AWS -->
<div>
<span class="font-semibold block">Upload image to AWS</span>
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/BootcAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export abstract class BootcApi {
abstract generateUniqueBuildID(name: string): Promise<string>;
abstract openLink(link: string): Promise<void>;
abstract isLinux(): Promise<boolean>;
abstract getUidGid(): Promise<string>;
abstract telemetryLogUsage(eventName: string, data?: Record<string, unknown> | undefined): Promise<void>;
abstract telemetryLogError(eventName: string, data?: Record<string, unknown> | undefined): Promise<void>;
}
1 change: 1 addition & 0 deletions packages/shared/src/models/bootc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface BootcBuildInfo {
engineId: string;
type: BuildType[];
folder: string;
chown?: string;
buildConfigFilePath?: string;
filesystem?: string;
arch?: string;
Expand Down

0 comments on commit 357038a

Please sign in to comment.