From 357038a7b7ec876cc73896a6e41c9044dd96e4b3 Mon Sep 17 00:00:00 2001
From: Charlie Drage
Date: Tue, 6 Aug 2024 13:48:45 -0400
Subject: [PATCH] feat: add chown (linux only) (#695)
* feat: add chown (linux only)
### What does this PR do?
* Adds chown to the advanced settings page for build
* Only applicable to Linux (see:
https://github.com/osbuild/bootc-image-builder/issues/576)
### Screenshot / video of UI
### What issues does this PR fix or reference?
Closes https://github.com/containers/podman-desktop-extension-bootc/issues/472
### How to test this PR?
1. Be on Linux
2. Build an image
3. See that it is chown (ex. ls -l /outputdir)
Signed-off-by: Charlie Drage
* by default fill in uid and gid
Signed-off-by: Charlie Drage
---------
Signed-off-by: Charlie Drage
---
packages/backend/src/api-impl.ts | 6 ++-
packages/backend/src/build-disk-image.spec.ts | 25 +++++++++++++
packages/backend/src/build-disk-image.ts | 5 +++
packages/backend/src/machine-utils.ts | 9 +++++
packages/frontend/src/Build.spec.ts | 3 ++
packages/frontend/src/Build.svelte | 37 +++++++++++++++++++
packages/shared/src/BootcAPI.ts | 1 +
packages/shared/src/models/bootc.ts | 1 +
8 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/packages/backend/src/api-impl.ts b/packages/backend/src/api-impl.ts
index 09bf10eb..0c00f1df 100644
--- a/packages/backend/src/api-impl.ts
+++ b/packages/backend/src/api-impl.ts
@@ -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;
@@ -244,6 +244,10 @@ export class BootcApiImpl implements BootcApi {
return isLinux();
}
+ async getUidGid(): Promise {
+ 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
diff --git a/packages/backend/src/build-disk-image.spec.ts b/packages/backend/src/build-disk-image.spec.ts
index 98a422c6..d0ad03ad 100644
--- a/packages/backend/src/build-disk-image.spec.ts
+++ b/packages/backend/src/build-disk-image.spec.ts
@@ -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);
+});
diff --git a/packages/backend/src/build-disk-image.ts b/packages/backend/src/build-disk-image.ts
index b9b0b8cd..09e7a1a2 100644
--- a/packages/backend/src/build-disk-image.ts
+++ b/packages/backend/src/build-disk-image.ts
@@ -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;
}
diff --git a/packages/backend/src/machine-utils.ts b/packages/backend/src/machine-utils.ts
index b8026a14..d5fe9617 100644
--- a/packages/backend/src/machine-utils.ts
+++ b/packages/backend/src/machine-utils.ts
@@ -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 {
+ const { stdout: uidOutput } = await extensionApi.process.exec('id', ['-u']);
+ const { stdout: gidOutput } = await extensionApi.process.exec('id', ['-g']);
+ return `${uidOutput.trim()}:${gidOutput.trim()}`;
+}
diff --git a/packages/frontend/src/Build.spec.ts b/packages/frontend/src/Build.spec.ts
index 388dd4f4..d305b6d0 100644
--- a/packages/frontend/src/Build.spec.ts
+++ b/packages/frontend/src/Build.spec.ts
@@ -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();
});
diff --git a/packages/frontend/src/Build.svelte b/packages/frontend/src/Build.svelte
index a37aa7d8..3201e78a 100644
--- a/packages/frontend/src/Build.svelte
+++ b/packages/frontend/src/Build.svelte
@@ -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
@@ -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) {
@@ -193,6 +205,7 @@ async function buildBootcImage() {
type: buildType,
arch: buildArch,
filesystem: buildFilesystem,
+ chown: buildChown,
awsAmiName: awsAmiName,
awsBucket: awsBucket,
awsRegion: awsRegion,
@@ -291,6 +304,10 @@ onMount(async () => {
await fillBuildOptions(historyInfo);
await fillArchitectures(historyInfo);
+ if (isLinux) {
+ await fillChownOption();
+ }
+
validate();
});
@@ -689,6 +706,26 @@ export function goToHomePage(): void {
+
+ {#if isLinux}
+
+
+
+
+
+
+ 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.
+